Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Property filter token groups #2626

Merged
merged 9 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions pages/property-filter/common-props.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { PropertyFilterProps } from '~components/property-filter';
import { I18nStringsTokenGroups } from '~components/property-filter/interfaces';

import {
DateForm,
Expand Down Expand Up @@ -184,9 +183,7 @@ export const i18nStrings: PropertyFilterProps.I18nStrings = {

formatToken,
removeTokenButtonAriaLabel: token => `Remove token, ${formatToken(token)}`,
};

export const i18nStringsTokenGroups: I18nStringsTokenGroups = {
groupEditAriaLabel: group => `Edit group with ${group.tokens.length} tokens`,
tokenEditorTokenActionsAriaLabel: token => `Filter remove actions for ${formatToken(token)}`,
tokenEditorTokenRemoveAriaLabel: token => `Remove filter, ${formatToken(token)}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Button from '~components/button';
import Header from '~components/header';
import I18nProvider from '~components/i18n';
import messages from '~components/i18n/messages/all.en';
import PropertyFilter from '~components/property-filter/internal';
import PropertyFilter from '~components/property-filter';
import SplitPanel from '~components/split-panel';
import Table from '~components/table';

Expand Down
5 changes: 1 addition & 4 deletions pages/property-filter/token-editor.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import React from 'react';

import PropertyFilter from '~components/property-filter';
import { PropertyFilterProps } from '~components/property-filter/interfaces';
import PropertyFilterInternal from '~components/property-filter/internal';

import ScreenshotArea from '../utils/screenshot-area';
import {
columnDefinitions,
filteringProperties as commonFilteringProperties,
i18nStrings,
i18nStringsTokenGroups,
labels,
} from './common-props';

Expand All @@ -28,7 +26,6 @@ const commonProps = {
filteringProperties,
filteringOptions: [],
i18nStrings,
i18nStringsTokenGroups,
countText: '5 matches',
disableFreeTextFiltering: false,
virtualScroll: true,
Expand Down Expand Up @@ -102,7 +99,7 @@ export default function () {
{...commonProps}
freeTextFiltering={{ operators: [':', '!:', '=', '!=', '^', '!^'] }}
/>
<PropertyFilterInternal
<PropertyFilter
className="property-filter-group-editor"
query={{
tokens: [
Expand Down
62 changes: 58 additions & 4 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12216,6 +12216,14 @@ in order to prevent the user from changing the filtering query.",
"optional": true,
"type": "boolean",
},
{
"defaultValue": "false",
"description": "Activates token grouping mechanism to support tokens nesting (up to one level).
When \`true\`, the \`query.tokens\` property is ignored and \`query.tokenGroups\` is used instead.",
"name": "enableTokenGroups",
"optional": true,
"type": "boolean",
},
{
"description": "By default, the dropdown height is constrained to fit inside the height of its next scrollable container element.
Enabling this property will allow the dropdown to extend beyond that container by using fixed positioning and
Expand Down Expand Up @@ -12341,6 +12349,7 @@ Use the \`onLoadItems\` event to perform a recovery action (for example, retryin
"type": "PropertyFilterProps.FreeTextFiltering",
},
{
"defaultValue": "false",
"description": "If hideOperations it set, the indicator of the operation (that is, \`and\` or \`or\`) and the selection of operations
(applied to the property and value token) are hidden from the user. Only use when you have a custom
filtering logic which combines tokens in different way than the default one. When used, ensure that
Expand Down Expand Up @@ -12410,6 +12419,11 @@ operations are communicated to the user in another way.",
"optional": true,
"type": "(token: PropertyFilterProps.FormattedToken) => string",
},
{
"name": "groupEditAriaLabel",
"optional": true,
"type": "(group: PropertyFilterProps.FormattedTokenGroup) => string",
},
{
"name": "groupPropertiesText",
"optional": true,
Expand Down Expand Up @@ -12500,6 +12514,46 @@ operations are communicated to the user in another way.",
"optional": true,
"type": "(token: PropertyFilterProps.FormattedToken) => string",
},
{
"name": "tokenEditorAddExistingTokenAriaLabel",
"optional": true,
"type": "(token: PropertyFilterProps.FormattedToken) => string",
},
{
"name": "tokenEditorAddExistingTokenLabel",
"optional": true,
"type": "(token: PropertyFilterProps.FormattedToken) => string",
},
{
"name": "tokenEditorAddNewTokenLabel",
"optional": true,
"type": "string",
},
{
"name": "tokenEditorAddTokenActionsAriaLabel",
"optional": true,
"type": "string",
},
{
"name": "tokenEditorTokenActionsAriaLabel",
"optional": true,
"type": "(token: PropertyFilterProps.FormattedToken) => string",
},
{
"name": "tokenEditorTokenRemoveAriaLabel",
"optional": true,
"type": "(token: PropertyFilterProps.FormattedToken) => string",
},
{
"name": "tokenEditorTokenRemoveFromGroupLabel",
"optional": true,
"type": "string",
},
{
"name": "tokenEditorTokenRemoveLabel",
"optional": true,
"type": "string",
},
{
"name": "tokenLimitShowFewer",
"optional": true,
Expand Down Expand Up @@ -12537,14 +12591,14 @@ use the \`id\` attribute, consider setting it on a parent element instead.",
"type": "string",
},
{
"description": "An object representing the current query displayed in the property filter. Has two properties: \`tokens\` and \`operation\`.
\`tokens\` is an array of objects that will be displayed to the user beneath the filtering input.
"description": "An object representing the current query displayed in the property filter. Has three properties: \`operation\`, \`tokens\`, and \`tokenGroups\`.
The \`operation\` property has two valid values: "and", "or", and controls the join operation to be applied between tokens when filtering the items.
The \`tokens\` property is an array of objects that will be displayed to the user beneath the filtering input. When \`enableTokenGroups=true\`, the
\`tokenGroups\` property is used instead, which supports nested tokens.
Each token has the following properties:
* value [string]: The string value of the token to be used as a filter.
* propertyKey [string]: The key of the corresponding property in filteringProperties.
* operator ['<' | '<=' | '>' | '>=' | ':' | '!:' | '=' | '!=' | '^' | '!^']: The operator which indicates how to filter the dataset using this token.

\`operation\` has two valid values [and, or] and controls the join operation to be applied between tokens when filtering the items.
",
"inlineType": {
"name": "PropertyFilterProps.Query",
Expand Down
17 changes: 1 addition & 16 deletions src/property-filter/__tests__/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,7 @@
import React, { useState } from 'react';

import PropertyFilter from '../../../lib/components/property-filter';
import PropertyFilterInternal, { PropertyFilterInternalProps } from '../../../lib/components/property-filter/internal';
import {
FilteringProperty,
I18nStrings,
I18nStringsTokenGroups,
InternalFilteringProperty,
PropertyFilterProps,
Token,
} from '../interfaces';
import { FilteringProperty, I18nStrings, InternalFilteringProperty, PropertyFilterProps, Token } from '../interfaces';

export const i18nStrings: I18nStrings = {
dismissAriaLabel: 'Dismiss',
Expand Down Expand Up @@ -51,9 +43,7 @@ export const i18nStrings: I18nStrings = {
formatToken: token => `${token.propertyLabel} ${formatOperator(token.operator)} ${token.value}`,
removeTokenButtonAriaLabel: (token: Token) =>
'Remove token ' + token.propertyKey + ' ' + formatOperator(token.operator) + ' ' + token.value,
};

export const i18nStringsTokenGroups: I18nStringsTokenGroups = {
groupEditAriaLabel: group =>
'Edit filter, ' +
group.tokens
Expand Down Expand Up @@ -144,8 +134,3 @@ export function StatefulPropertyFilter(props: Omit<PropertyFilterProps, 'onChang
const [query, setQuery] = useState<PropertyFilterProps.Query>(props.query);
return <PropertyFilter {...props} query={query} onChange={e => setQuery(e.detail)} />;
}

export function StatefulInternalPropertyFilter(props: Omit<PropertyFilterInternalProps, 'onChange'>) {
const [query, setQuery] = useState<PropertyFilterInternalProps['query']>(props.query);
return <PropertyFilterInternal {...props} query={query} onChange={e => setQuery(e.detail)} />;
}
6 changes: 2 additions & 4 deletions src/property-filter/__tests__/property-filter-i18n.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import { act, render } from '@testing-library/react';

import TestI18nProvider from '../../../lib/components/i18n/testing';
import PropertyFilter from '../../../lib/components/property-filter';
import InternalPropertyFilter from '../../../lib/components/property-filter/internal';
import createWrapper, { ElementWrapper, PropertyFilterWrapper } from '../../../lib/components/test-utils/dom';
import { PropertyFilterWrapperInternal } from '../../../lib/components/test-utils/dom/property-filter/index.js';
import { createDefaultProps } from './common';

import styles from '../../../lib/components/property-filter/styles.selectors.js';
Expand Down Expand Up @@ -280,7 +278,7 @@ describe('i18n', () => {
},
}}
>
<InternalPropertyFilter
<PropertyFilter
{...defaultProps}
i18nStrings={{}}
query={{
Expand All @@ -306,7 +304,7 @@ describe('i18n', () => {
/>
</TestI18nProvider>
);
const wrapper = new PropertyFilterWrapperInternal(createWrapper(container).findPropertyFilter()!.getElement());
const wrapper = createWrapper(container).findPropertyFilter()!;
const token = (index: number) => wrapper.findTokens()[index];
const groupToken = (index: number, inGroupIndex: number) => token(index).findGroupTokens()[inGroupIndex];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@ import { act, fireEvent, render as reactRender } from '@testing-library/react';

import TestI18nProvider from '../../../lib/components/i18n/testing';
import { useMobile } from '../../../lib/components/internal/hooks/use-mobile';
import { FilteringOption, FilteringProperty } from '../../../lib/components/property-filter/interfaces';
import PropertyFilterInternal, { PropertyFilterInternalProps } from '../../../lib/components/property-filter/internal';
import createWrapper from '../../../lib/components/test-utils/dom';
import { PropertyFilterWrapperInternal } from '../../../lib/components/test-utils/dom/property-filter';
import PropertyFilter from '../../../lib/components/property-filter';
import {
createDefaultProps,
i18nStrings,
i18nStringsTokenGroups,
providedI18nStrings,
StatefulInternalPropertyFilter,
} from './common';
FilteringOption,
FilteringProperty,
PropertyFilterProps,
} from '../../../lib/components/property-filter/interfaces';
import createWrapper from '../../../lib/components/test-utils/dom';
import { createDefaultProps, i18nStrings, providedI18nStrings, StatefulPropertyFilter } from './common';

jest.mock('../../../lib/components/internal/hooks/use-mobile', () => ({
...jest.requireActual('../../../lib/components/internal/hooks/use-mobile'),
Expand Down Expand Up @@ -71,26 +68,25 @@ const filteringOptions: readonly FilteringOption[] = [
{ propertyKey: 'default-operator', value: 'value' },
];

const defaultProps: PropertyFilterInternalProps = {
const defaultProps: PropertyFilterProps = {
filteringOptions: [],
customGroupsText: [],
disableFreeTextFiltering: false,
i18nStringsTokenGroups,
...createDefaultProps(filteringProperties, filteringOptions),
};

function renderComponent(props?: Partial<PropertyFilterInternalProps>, withI18nProvider = false) {
function renderComponent(props?: Partial<PropertyFilterProps>, withI18nProvider = false) {
return withI18nProvider
? reactRender(
<TestI18nProvider messages={providedI18nStrings}>
<PropertyFilterInternal {...defaultProps} {...props} i18nStrings={{}} i18nStringsTokenGroups={{}} />
<PropertyFilter {...defaultProps} {...props} i18nStrings={{}} />
</TestI18nProvider>
)
: reactRender(<PropertyFilterInternal {...defaultProps} {...props} />);
: reactRender(<PropertyFilter {...defaultProps} {...props} />);
}

function openEditor(tokenIndex: number, options: { expandToViewport?: boolean; isMobile?: boolean }) {
const propertyFilter = new PropertyFilterWrapperInternal(createWrapper().findPropertyFilter()!.getElement());
const propertyFilter = createWrapper().findPropertyFilter()!;
const token = propertyFilter.findTokens()[tokenIndex];
if (token.findEditButton()) {
token.findEditButton()!.click();
Expand All @@ -104,7 +100,7 @@ function findEditor(
tokenIndex: number,
{ expandToViewport = false, isMobile = false }: { expandToViewport?: boolean; isMobile?: boolean }
) {
const propertyFilter = new PropertyFilterWrapperInternal(createWrapper().findPropertyFilter()!.getElement());
const propertyFilter = createWrapper().findPropertyFilter()!;
const editor = propertyFilter.findTokens()[tokenIndex].findEditorDropdown({ expandToViewport })!;
return editor
? {
Expand Down Expand Up @@ -450,12 +446,12 @@ describe('token editor with groups', () => {
jest.mocked(useMobile).mockReturnValue(false);
});

function render(props: Partial<PropertyFilterInternalProps>) {
function render(props: Partial<PropertyFilterProps>) {
return renderComponent({ enableTokenGroups: true, ...props });
}

function renderStateful(props?: Partial<PropertyFilterInternalProps>) {
return reactRender(<StatefulInternalPropertyFilter {...defaultProps} {...props} enableTokenGroups={true} />);
function renderStateful(props?: Partial<PropertyFilterProps>) {
return reactRender(<StatefulPropertyFilter {...defaultProps} {...props} enableTokenGroups={true} />);
}

test('changes filter property', () => {
Expand Down
Loading
Loading