Skip to content

Commit

Permalink
[FEATURE] Permettre de trier les données de PixTable (PIX-15560)
Browse files Browse the repository at this point in the history
  • Loading branch information
pix-service-auto-merge authored Dec 13, 2024
2 parents 7467726 + a00d884 commit 54ff81e
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 8 deletions.
14 changes: 12 additions & 2 deletions addon/components/pix-table-column.hbs
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
{{#if this.displayHeader}}
<th scope="col" ...attributes>
{{yield to="header"}}
<th scope="col" ...attributes aria-sort={{this.ariaSort}}>
<div class="pix-table-header-container">
{{yield to="header"}}
{{#if this.sortable}}
<PixIconButton
@ariaLabel={{this.iconLabel}}
@iconName={{this.iconName}}
@triggerAction={{@onSort}}
@size="small"
/>
{{/if}}
</div>
</th>
{{else}}
<td ...attributes class={{this.typeClass}}>
Expand Down
71 changes: 69 additions & 2 deletions addon/components/pix-table-column.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,77 @@ export default class PixTableColumn extends Component {
return this.args.context === 'header';
}

get type() {
return this.args.type ?? 'text';
}

get sortable() {
return Boolean(this.args.onSort);
}

get sortOrder() {
if (this.args.sortOrder === undefined) {
return undefined;
}
const correctSortOrders = ['asc', 'desc', null];
warn(
'PixTableColumn: you need to provide a valid sortOrder',
correctSortOrders.includes(this.args.sortOrder),
{
id: 'pix-ui.table-column.sortOrder.not-valid',
},
);
return this.args.sortOrder;
}

get iconName() {
const isText = this.type === 'text';
if (!this.sortOrder) {
return isText ? 'sortAz' : 'sort';
}
if (this.sortOrder === 'asc') {
return isText ? 'sortAzAsc' : 'sortAsc';
}
return isText ? 'sortAzDesc' : 'sortDesc';
}

get iconLabel() {
warn(
'PixTableColumn: parameters `@ariaLabelDefaultSort`, `@ariaLabelSortDesc` and `@ariaLabelSortAsc` are required for sort buttons',
![
this.args.ariaLabelDefaultSort,
this.args.ariaLabelSortDesc,
this.args.ariaLabelSortAsc,
].includes(undefined),
{
id: 'pix-ui.pix-table-column.sortAriaLabels.required',
},
);
if (!this.sortOrder) {
return this.args.ariaLabelDefaultSort;
}
if (this.sortOrder === 'asc') {
return this.args.ariaLabelSortDesc;
}
return this.args.ariaLabelSortAsc;
}

get ariaSort() {
if (!this.sortable) {
return undefined;
}
if (!this.sortOrder) {
return 'none';
}
if (this.sortOrder === 'asc') {
return 'ascending';
}
return 'descending';
}

get typeClass() {
const correctTypes = ['number', 'text'];
const type = this.args.type ?? 'text';
warn('PixTableColumn: you need to provide a valid type', correctTypes.includes(type), {
warn('PixTableColumn: you need to provide a valid type', correctTypes.includes(this.type), {
id: 'pix-ui.table-column.type.incorrect',
});
if (this.args.type === 'number') {
Expand Down
7 changes: 7 additions & 0 deletions addon/styles/_pix-table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,15 @@
}
}

.pix-table-header-container {
display: flex;
gap: var(--pix-spacing-1x);
align-items: center;
}

th {
text-align: start;
vertical-align: middle;
}

td, th {
Expand Down
42 changes: 42 additions & 0 deletions app/stories/pix-table-column.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,48 @@ Une colonne d'un [PixTable](/docs/data-display-table--docs), gère l'affichage d
</PixTable>
```

## Tri

<Story of={ComponentStories.Sorted} height={400} />

```html
<PixTable @data={{this.data}} @caption={{this.caption}}>
<:columns as |row context|>
<PixTableColumn
@context={{context}}
@onSort={{this.sortAz}}
@sortOrder={{this.sortOrderAz}}
@ariaLabelDefaultSort={{this.ariaLabelDefaultSort}}
@ariaLabelSortAsc={{this.ariaLabelSortAsc}}
@ariaLabelSortDesc={{this.ariaLabelSortDesc}}
>
<:header>
Nom
</:header>
<:cell>
{{row.name}}
</:cell>
</PixTableColumn>
<PixTableColumn
@context={{context}}
@type="number"
@onSort={{this.sortNum}}
@sortOrder={{this.sortOrderNum}}
@ariaLabelDefaultSort={{this.ariaLabelDefaultSort}}
@ariaLabelSortAsc={{this.ariaLabelSortAsc}}
@ariaLabelSortDesc={{this.ariaLabelSortDesc}}
>
<:header>
Age
</:header>
<:cell>
{{row.age}}
</:cell>
</PixTableColumn>
</:columns>
</PixTable>
```

## Arguments

<ArgTypes />
98 changes: 98 additions & 0 deletions app/stories/pix-table-column.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,43 @@ export default {
description: 'Propriété a récupérer depuis le block element `<:columns>` du PixTable parent.',
type: { name: 'privé', required: true },
},
onSort: {
name: 'onSort',
description:
"Fonction appelée en cas de clic sur le bouton de tri d'une colonne. Sa présence détermine si le bouton de tri est affiché ou non. Le tri est à implémenter soi-même.",
type: { name: 'function', required: false },
},
sortOrder: {
name: 'sortOrder',
description:
"Statut du tri de la colonne. À gérer du côté de l'application.<br> ⚠️ Obligatoire si `@onSort` est utilisé ⚠️",
options: ['asc', 'desc', null],
control: {
type: 'select',
},
type: {
name: '"asc" | "desc" | null',
required: false,
},
},
ariaLabelDefaultSort: {
name: 'ariaLabelDefaultSort',
description:
"Label du bouton de tri, lorsqu'aucun tri n'est appliqué.<br> ⚠️ Obligatoire si `@onSort` est utilisé ⚠️",
type: { name: 'string', required: false },
},
ariaLabelSortAsc: {
name: 'ariaLabelSortAsc',
description:
'Label du bouton de tri (pour trier en ordre ascendant), lorsque le tri descendant est appliqué.<br> ⚠️ Obligatoire si `@onSort` est utilisé ⚠️',
type: { name: 'string', required: false },
},
ariaLabelSortDesc: {
name: 'ariaLabelSortDesc',
description:
'Label du bouton de tri (pour trier en ordre descendant), lorsque le tri ascendant est appliqué.<br> ⚠️ Obligatoire si `@onSort` est utilisé ⚠️',
type: { name: 'string', required: false },
},
type: {
defaultValue: {
summary: 'text',
Expand Down Expand Up @@ -74,3 +111,64 @@ Default.args = {
},
],
};

const TemplateSort = (args) => {
return {
template: hbs`<PixTable @data={{this.data}} @caption={{this.caption}}>
<:columns as |row context|>
<PixTableColumn
@context={{context}}
@onSort={{this.sort}}
@sortOrder={{this.sortOrder}}
@ariaLabelDefaultSort={{this.ariaLabelDefaultSort}}
@ariaLabelSortAsc={{this.ariaLabelSortAsc}}
@ariaLabelSortDesc={{this.ariaLabelSortDesc}}
>
<:header>
Nom
</:header>
<:cell>
{{row.name}}
</:cell>
</PixTableColumn>
<PixTableColumn
@context={{context}}
@type='number'
@onSort={{this.sort}}
@sortOrder={{this.sortOrder}}
@ariaLabelDefaultSort={{this.ariaLabelDefaultSort}}
@ariaLabelSortAsc={{this.ariaLabelSortAsc}}
@ariaLabelSortDesc={{this.ariaLabelSortDesc}}
>
<:header>
Age
</:header>
<:cell>
{{row.age}}
</:cell>
</PixTableColumn>
</:columns>
</PixTable>`,
context: args,
};
};

export const Sorted = TemplateSort.bind({});
Sorted.args = {
caption: 'Description du tableau',
data: [
{
name: 'jean',
age: 15,
},
{
name: 'brian',
age: 25,
},
],
sort() {},
sortOrder: 'asc',
ariaLabelDefaultSort: 'click pour trier',
ariaLabelSortAsc: 'click pour trier en ordre ascendant',
ariaLabelSortDesc: 'click pour trier en ordre descendant',
};
3 changes: 3 additions & 0 deletions app/stories/pix-table.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,7 @@ Default.args = {
age: 25,
},
],
onNameSort: () => {
alert('Fonctionnalité seulement disponible en local sur dummy');
},
};
59 changes: 59 additions & 0 deletions tests/dummy/app/controllers/table-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class TablePage extends Controller {
@tracked
nameSortOrder = null;
@tracked
numSortOrder = null;

variant = 'orga';

@tracked
data = [
{
name: 'jean',
description: 'fort au jungle speed',
age: 15,
},
{
name: 'brian',
description: 'travail au peach pit',
age: 25,
},
];

caption = 'Titre de mon tableau';

@action
onNameSort() {
this.resetOrders('name');
if (this.nameSortOrder === 'asc') {
this.data = this.data.sort((a, b) => b.name.localeCompare(a.name));
this.nameSortOrder = 'desc';
} else {
this.data = this.data.sort((a, b) => a.name.localeCompare(b.name));
this.nameSortOrder = 'asc';
}
}

@action
onNumSort() {
this.resetOrders('num');
if (this.numSortOrder === 'asc') {
this.data = this.data.sort((a, b) => b.age - a.age);
this.numSortOrder = 'desc';
} else {
this.data = this.data.sort((a, b) => a.age - b.age);
this.numSortOrder = 'asc';
}
}

resetOrders(except) {
for (const key of ['num', 'name']) {
if (key === except) continue;
this[`${key}SortOrder`] = null;
}
}
}
1 change: 1 addition & 0 deletions tests/dummy/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Router.map(function () {
this.route('select-page', { path: '/select' });
this.route('sidebar-page', { path: '/sidebar' });
this.route('tooltip-page', { path: '/tooltip' });
this.route('table-page', { path: '/table' });
});
1 change: 1 addition & 0 deletions tests/dummy/app/templates/application.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<PixNavigationButton @route="select-page" @icon="book">select</PixNavigationButton>
<PixNavigationButton @route="sidebar-page" @icon="doorOpen">Sidebar</PixNavigationButton>
<PixNavigationButton @route="tooltip-page" @icon="signpost">tooltip</PixNavigationButton>
<PixNavigationButton @route="table-page" @icon="assignment">Table</PixNavigationButton>

<PixNavigationButton href="https://pix.fr" @icon="book">Documentation</PixNavigationButton>
<PixNavigationButton href="https://pix.fr" title="Pix.fr" @target="_blank" @icon="help">Centre
Expand Down
Loading

0 comments on commit 54ff81e

Please sign in to comment.