From 16e614f4f8a7076522236aab822f1ade87a6b558 Mon Sep 17 00:00:00 2001 From: Snorre Kim Date: Fri, 18 Oct 2024 14:51:40 +0200 Subject: [PATCH] Improve DrawerList, Autocomplete, Dropdown, props documentation. Add missing 'selectedKey'. Allow index 'value' if no matching 'selectedKey' --- .../components/autocomplete/properties.mdx | 55 +------- .../docs/uilib/components/dropdown/events.mdx | 12 +- .../uilib/components/dropdown/properties.mdx | 45 +------ .../fragments/drawer-list/_prop-data.mdx | 126 ++++++++++++++++++ .../fragments/drawer-list/properties.mdx | 3 + .../src/shared/parts/PropertiesTable.tsx | 2 +- .../autocomplete/AutocompleteDocs.ts | 2 +- .../dropdown/__tests__/Dropdown.test.tsx | 2 + .../src/fragments/drawer-list/DrawerList.js | 4 + .../fragments/drawer-list/DrawerListDocs.ts | 38 +++++- .../drawer-list/DrawerListHelpers.js | 15 ++- 11 files changed, 190 insertions(+), 114 deletions(-) create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx index 41bacf01df2..2732054046d 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/autocomplete/properties.mdx @@ -6,6 +6,7 @@ import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/Transla import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' import { autocompleteProperties } from '@dnb/eufemia/src/components/autocomplete/AutocompleteDocs' import { DrawerListProperties } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs' +import DrawerListDataDoc from '../fragments/drawer-list/_prop-data.mdx' ## Properties @@ -17,59 +18,7 @@ You may check out the [DrawerList Properties](#drawerlist-properties) down below -## Data structure - -```js -// 1. as array -const data = [ - // Every data item can, beside "content" - contain what ever - { - // (optional) can be what ever - selected_key: 'key_0', - - // (optional) is show instead of "content", once selected - selected_value: 'Item 1 Value', - suffix_value: 'Addition 1', - - // Item content as a string, array or React Element - content: 'Item 1 Content', - }, - - // more items ... - { - selected_key: 'key_1', - content: ( - <> - - Searchable content - - ), - }, - { - selected_key: 'key_2', - selected_value: 'Item 3 Value', - suffix_value: 'Addition 3', - content: ( - - - Searchable content - - ), - }, - { - selected_key: 'key_3', - selected_value: 'Item 4 Value', - suffix_value: 'Addition 4', - content: ['Item 4 Content A', <>Custom Component], - }, -] - -// 2. as object -const data = { - a: 'A', - b: 'B', -} -``` + ## Translations diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx index 8f04391fb14..368183e0d09 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/events.mdx @@ -4,12 +4,12 @@ showTabs: true ## Events -| Events | Description | -| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `on_change` | _(optional)_ will be called on state changes made by the user. Returns an object with the new selected `data` item `{ data, event, attributes, value }`. | -| `on_select` | _(optional)_ will be called once the user selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }`. The **active_item** property is the currently selected item by keyboard navigation | -| `on_show` | _(optional)_ will be called once the user presses the dropdown. Returns the data item `{ data, attributes }`. | -| `on_hide` | _(optional)_ will be called once the user presses the dropdown again, or clicks somewhere else. Returns the data item `{ data, attributes }`. | +| Events | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `on_change` | _(optional)_ will be called on state changes made by the user. Returns an object with the new selected `data` item `{ data, event, attributes, value }`. | +| `on_select` | _(optional)_ will be called once the user focuses or selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }`. The **active_item** property is the currently selected item by keyboard navigation | +| `on_show` | _(optional)_ will be called once the user presses the dropdown. Returns the data item `{ data, attributes }`. | +| `on_hide` | _(optional)_ will be called once the user presses the dropdown again, or clicks somewhere else. Returns the data item `{ data, attributes }`. | ### The `on_change` vs `on_select` difference diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx index 46db4976934..7f8fe31a14e 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/dropdown/properties.mdx @@ -5,6 +5,7 @@ showTabs: true import TranslationsTable from 'dnb-design-system-portal/src/shared/parts/TranslationsTable' import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' import { DrawerListProperties } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs' +import DrawerListDataDoc from '../fragments/drawer-list/_prop-data.mdx' ## Properties @@ -54,49 +55,7 @@ Should either be an index (integer) of the data array or a key – defined by `s If `data` is an object, use the object key as the `value` to define the selected item. Can be a string or integer. -## Data structure - -```js -// 1. as array -const data = [ - // Every data item can, beside "content" - contain what ever - { - // (optional) can be what ever - selectedKey: 'key_0', - - // (optional) is show instead of "content", once selected - selected_value: 'Item 1 Value', - suffix_value: 'Addition 1', - - // Item content as a string or array - content: 'Item 1 Content', - }, - - // more items ... - { - selectedKey: 'key_1', - content: ['Item 2 Value', 'Item 2 Content'], - }, - { - selectedKey: 'key_2', - selected_value: 'Item 3 Value', - suffix_value: 'Addition 3', - content: ['Item 3 Content A', 'Item 3 Content B'], - }, - { - selectedKey: 'key_3', - selected_value: 'Item 4 Value', - suffix_value: 'Addition 4', - content: ['Item 4 Content A', <>Custom Component], - }, -] - -// 2. as object -const data = { - a: 'A', - b: 'B', -} -``` + ## Translations diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx new file mode 100644 index 00000000000..263d0071a70 --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/_prop-data.mdx @@ -0,0 +1,126 @@ +import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' +import { DrawerListItem } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs' + +## Prop `data` + +Note: even if we do not cover it here, the `data` prop can also accept a function that returns `data` or a JSON string of the `data`. + +The `data` can be structured in two main ways: as an object, or as an array. An array is preferred as it gives you the most options. + +### `data` as an array + +```ts +// an array can contain complex items and offers the most control +const data = [ + { + content: "Item 1", + }, + { + content: Item 2 + }, + { + content: ["Item 3", "Line 2", Line 3] + }, + { + content: ['Main account', '1234 12 12345'], + selected_value: 'Main account (605,22 kr)', + suffix_value: '605,22 kr', + }, + { + content: ['Old account', Closed], + suffix_value: '0,00 kr', + }, +] + +// If you only use the `content` property, you can use it directly in the. +// This list is identical to the one above: +const data = [ + "Item 1", + Item 2, + ["Item 3", "Line 2", Line 3], + { + content: ['Main account', '1234 12 12345'], + selected_value: 'Main account (605,22 kr)', + suffix_value: '605,22 kr', + }, + { + content: ['Old account', Closed], + suffix_value: '0,00 kr', + }, +] + +const onChange = ({ data, value }) => { + console.log(data) // returns the item as it appears in the array + console.log(value) // returns the array index of the item +} +``` + +Each object in the array have the following properties: + + + +### `data` as an object + +A simpler alternative, but with less options + +```ts +// Each entry can contain the same type of value as the `content` property of an the array type +// property of an array item +const data = { + first: "Item 1",, + second: Item 2, + last: ["Item 3", "Line 2", Line 3], +} + +const onChange = ({ data, value }) => { + console.log(data) // returns an object representing the item: + // { + // selectedKey: 'first', + // value: 'first', + // content: 'Item 1', + // type: 'object' + // } + + console.log(value) // returns the key ("first", "second", or "last"), instead of an index + +} + +``` + +### `data` types overview + +The following is an overview of all the types that the `data` prop accepts. (These are not actual names of actual types in the library.) + +```ts +// The visual content that is shown in one DrawerList item. +// An array can be used to define multiple lines. +type CONTENT = string | React.Node | (string | React.Node)[] + +// An array item +type ITEM = { + content: CONTENT + selectedKey?: string | number + selected_value?: CONTENT + suffix_value?: string | React.Node +} + +// `data` as an array. A list of "ITEM" types is preferred, +// but the "CONTENT" type can be useful for simple lists. +type ARRAY = (CONTENT | ITEM)[] + +// `data` as an object. Can only contain the "CONTENT" type. +// Each `key` behaves like the "ITEM"'s `selectedKey`. +type OBJECT = Record + +// An object or array that represents the entire DrawerList list. +type DATA = ARRAY | OBJECT + +// A JSON stringified object/array of the "DATA" type +type JSON_DATA = string + +// a dunction that returns an object/array of the "DATA" type +type FUNCTION_DATA = () => DATA + +// The final type of the `data` prop. +type data = DATA | FUNCTION_DATA | JSON_DATA +``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx index baf76d87302..7cd82e4e733 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/fragments/drawer-list/properties.mdx @@ -4,7 +4,10 @@ showTabs: true import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable' import { DrawerListProperties } from '@dnb/eufemia/src/fragments/drawer-list/DrawerListDocs' +import DrawerListDataDoc from './_prop-data.mdx' ## Properties + + diff --git a/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx b/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx index 52ea1f429b8..a22cbcf03eb 100644 --- a/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx +++ b/packages/dnb-design-system-portal/src/shared/parts/PropertiesTable.tsx @@ -179,7 +179,7 @@ function convertToCamelCase(doc: string, keys: string[]) { } export function formatName(name: string): React.ReactNode | string { - if (name.includes('/')) { + if (name.includes('[')) { return {name} } diff --git a/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts b/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts index f3e63e18783..83d399c7553 100644 --- a/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts +++ b/packages/dnb-eufemia/src/components/autocomplete/AutocompleteDocs.ts @@ -270,7 +270,7 @@ export const AutocompleteEvents: PropertiesTableProps = { status: 'optional', }, on_select: { - doc: 'Will be called once the users selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }` including [these methods](/uilib/components/autocomplete/events#dynamically-change-data). The "active_item" property is the currently selected item by keyboard navigation', + doc: 'Will be called once the users focuses or selects an item by a click or keyboard navigation. Returns an object with the new selected `data` item `{ data, event, attributes, value, active_item }` including [these methods](/uilib/components/autocomplete/events#dynamically-change-data). The "active_item" property is the currently selected item by keyboard navigation', type: 'function', status: 'optional', }, diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx b/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx index f0cb5ae620d..f636babfc48 100644 --- a/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx +++ b/packages/dnb-eufemia/src/components/dropdown/__tests__/Dropdown.test.tsx @@ -732,6 +732,7 @@ describe('Dropdown component', () => { data: { __id: 0, content: 'English', + selectedKey: 'en-GB', selected_key: 'en-GB', type: 'object', value: 'en-GB', @@ -750,6 +751,7 @@ describe('Dropdown component', () => { isTrusted: false, data: { content: 'Norsk', + selectedKey: 'nb-NO', selected_key: 'nb-NO', type: 'object', value: 'nb-NO', diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js index 1db30ea2001..ec301d5a08a 100644 --- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js +++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerList.js @@ -577,6 +577,10 @@ ItemContent.propTypes = { hash: PropTypes.string, children: PropTypes.oneOfType([PropTypes.node, PropTypes.object]), } +ItemContent.defaultProps = { + hash: '', + children: undefined, +} DrawerList.HorizontalItem = ({ className, ...props }) => ( DATA'], status: 'required', }, value: { - doc: 'Define a preselected data entry (index) or key inside an array item. Can be a string or integer.', + doc: 'Define a preselected `data` entry. In order of priority, `value` can be set to: object key (if `data` is an object), `selectedKey` prop (if `data` is an array), array index (if no `selectedKey`) or content (if `value` is a non-integer string).', type: ['string', 'number'], status: 'optional', }, @@ -165,7 +165,7 @@ export const DrawerListEvents: PropertiesTableProps = { status: 'optional', }, on_select: { - doc: 'Will be called once the user selects an item by a click or keyboard navigation.', + doc: 'Will be called once the user focuses or selects an item by a click or keyboard navigation.', type: 'function', status: 'optional', }, @@ -180,3 +180,31 @@ export const DrawerListEvents: PropertiesTableProps = { status: 'optional', }, } + +export const DrawerListItem: PropertiesTableProps = { + content: { + doc: 'Visual content in the list item', + type: ['string', 'React.node', '(string | React.Node)[]'], + status: 'optional', + }, + selectedKey: { + doc: 'If set, can be used instead of list index to set the `value` prop', + type: ['string', 'number'], + status: 'optional', + }, + selected_value: { + doc: 'Replaces the standard value output when selected. Only used in some implementations (Dropdown, Autocomplete).', + type: ['string', 'React.Node', '(string | React.Node)[]'], + status: 'optional', + }, + suffix_value: { + doc: 'Content placed to the right in the list item.', + type: ['string', 'React.node'], + status: 'optional', + }, + selected_key: { + doc: 'Use prop `selectedKey` instead', + type: ['string', 'number'], + status: 'deprecated', + }, +} diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js index 613b26edae2..850b28c2115 100644 --- a/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js +++ b/packages/dnb-eufemia/src/fragments/drawer-list/DrawerListHelpers.js @@ -290,6 +290,7 @@ export const normalizeData = (props) => { const list = [] for (const key in data) { list.push({ + selectedKey: key, selected_key: key, value: key, content: data[key], @@ -327,13 +328,16 @@ export const getCurrentIndex = (value, data) => { return data?.findIndex((cur) => parseCurrentValue(cur) === value) } // 2. if "selectedKey" is given in data, we now handle it as a value, and not an index. - else if (selectedKeyExists()) { - return data?.findIndex( + if (selectedKeyExists()) { + const index = data?.findIndex( (cur) => String(parseCurrentValue(cur)) === String(value) ) + if (index > -1) { + return index + } } - // 3. if is numeric, handle it as a index. - else if (!isNaN(parseFloat(value))) { + // 3. if is numeric, and no matching "selectedKey", handle it as a index. + if (!isNaN(parseFloat(value))) { return value } @@ -346,6 +350,7 @@ export const getCurrentIndex = (value, data) => { } if ( typeof data[i]?.selectedKey !== 'undefined' || + typeof data[i]?.selected_key !== 'undefined' || data[i]?.type === 'object' ) { return true @@ -398,7 +403,7 @@ export const getCurrentData = (item_index, data) => { data = (data && data.find(({ __id }) => __id == item_index)) || null if (data && data.__isTransformed) { - data = parseCurrentValue(data) + return data.content } return data