From 2474a2f7771020da7961c2ad87337aaca1c97762 Mon Sep 17 00:00:00 2001 From: Derek Leadbetter Date: Mon, 25 Jul 2022 14:28:05 -0400 Subject: [PATCH] UDCSL #28 - Creating BatchEdit and KeyValuePair components --- .../src/components/KeyValuePairs.js | 79 +++++++++++++++++++ packages/semantic-ui/src/hooks/BatchEdit.js | 57 +++++++++++++ packages/semantic-ui/src/i18n/en.json | 9 +++ packages/semantic-ui/src/index.js | 5 ++ .../src/semantic-ui/KeyValuePairs.stories.js | 39 +++++++++ 5 files changed, 189 insertions(+) create mode 100644 packages/semantic-ui/src/components/KeyValuePairs.js create mode 100644 packages/semantic-ui/src/hooks/BatchEdit.js create mode 100644 packages/storybook/src/semantic-ui/KeyValuePairs.stories.js diff --git a/packages/semantic-ui/src/components/KeyValuePairs.js b/packages/semantic-ui/src/components/KeyValuePairs.js new file mode 100644 index 00000000..900ef078 --- /dev/null +++ b/packages/semantic-ui/src/components/KeyValuePairs.js @@ -0,0 +1,79 @@ +// @flow + +import React, { type ComponentType } from 'react'; +import { Button, Grid, Input } from 'semantic-ui-react'; +import _ from 'underscore'; +import i18n from '../i18n/i18n'; +import withBatchEdit, { type BatchEditProps } from '../hooks/BatchEdit'; + +type Item = { + key: string, + value: string +}; + +type Props = BatchEditProps & { + items: Array, + onChange: (items: Array) => void +}; + +const KeyValuePairs: ComponentType = withBatchEdit((props: Props) => ( +
+
+)); + +export default KeyValuePairs; diff --git a/packages/semantic-ui/src/hooks/BatchEdit.js b/packages/semantic-ui/src/hooks/BatchEdit.js new file mode 100644 index 00000000..0a82cad7 --- /dev/null +++ b/packages/semantic-ui/src/hooks/BatchEdit.js @@ -0,0 +1,57 @@ +// @flow + +import React, { useCallback, type ComponentType } from 'react'; +import _ from 'underscore'; + +type Props = { + items: Array, + onChange: (items: Array) => void +}; + +const withBatchEdit = (WrappedComponent: ComponentType): any => (props: Props) => { + /** + * Adds a new item to the list. + * + * @type {(function(): void)|*} + */ + const onAddItem = useCallback(() => { + props.onChange([...props.items, {}]); + }, [props.items]); + + /** + * Removes the item at the passed index from the list. + * + * @type {(function(*): void)|*} + */ + const onRemoveItem = useCallback((findIndex) => { + props.onChange(_.reject(props.items, (item, index) => index === findIndex)); + }, [props.items]); + + /** + * Updates the passed attribute of the item at the passed index. + * + * @type {(function(number, string, ?Event, {value: *}): void)|*} + */ + const onUpdateItem = useCallback((findIndex: number, attribute: string, e: ?Event, { value }) => { + props.onChange(_.map(props.items, (item, index) => ( + index !== findIndex ? item : ({ ...item, [attribute]: value }) + ))); + }, [props.items]); + + return ( + + ); +}; + +export default withBatchEdit; + +export type BatchEditProps = { + onAddItem: () => void, + onRemoveItem: (index: number) => void, + onUpdateItem: (index: number, attribute: string, e: Event, data: any) => void +}; diff --git a/packages/semantic-ui/src/i18n/en.json b/packages/semantic-ui/src/i18n/en.json index c1e46230..eb4d3b25 100644 --- a/packages/semantic-ui/src/i18n/en.json +++ b/packages/semantic-ui/src/i18n/en.json @@ -21,6 +21,9 @@ "errors": { "title": "Oops!" }, + "labels": { + "noRecords": "No records." + }, "messages": { "error": { "header": "Oops!" @@ -126,6 +129,12 @@ "showKeyboard": "Show Keyboard" } }, + "KeyValuePairs": { + "labels": { + "key": "Key", + "value": "Value" + } + }, "LazyDocument": { "buttons": { "download": "Download" diff --git a/packages/semantic-ui/src/index.js b/packages/semantic-ui/src/index.js index 2cebdeb7..5f534dc8 100644 --- a/packages/semantic-ui/src/index.js +++ b/packages/semantic-ui/src/index.js @@ -38,6 +38,7 @@ export { default as ItemCollection } from './components/ItemCollection'; export { default as ItemList } from './components/ItemList'; export { default as Items } from './components/Items'; export { default as KeyboardField } from './components/KeyboardField'; +export { default as KeyValuePairs } from './components/KeyValuePairs'; export { default as LazyDocument } from './components/LazyDocument'; export { default as LazyImage } from './components/LazyImage'; export { default as LazyVideo } from './components/LazyVideo'; @@ -77,10 +78,14 @@ export { default as VideoPlayer } from './components/VideoPlayer'; export { default as VideoPlayerButton } from './components/VideoPlayerButton'; export { default as ViewXML } from './components/ViewXML'; +// Hooks +export { default as BatchEdit } from './hooks/BatchEdit'; + // Types export type { EditPageProps } from './components/EditPage'; export type { FileUploadProps } from './components/FileUploadModal'; export type { Props as ListProps } from './components/List'; +export type { BatchEditProps } from './hooks/BatchEdit'; // Constants export { SORT_ASCENDING, SORT_DESCENDING } from './components/DataList'; diff --git a/packages/storybook/src/semantic-ui/KeyValuePairs.stories.js b/packages/storybook/src/semantic-ui/KeyValuePairs.stories.js new file mode 100644 index 00000000..ffed24f3 --- /dev/null +++ b/packages/storybook/src/semantic-ui/KeyValuePairs.stories.js @@ -0,0 +1,39 @@ +// @flow + +import React, { useState } from 'react'; +import { withA11y } from '@storybook/addon-a11y'; +import { withKnobs } from '@storybook/addon-knobs'; +import KeyValuePairs from '../../../semantic-ui/src/components/KeyValuePairs'; + +export default { + title: 'Components/Semantic UI/KeyValuePairs', + decorators: [withA11y, withKnobs] +}; + +export const Default = () => { + const [value, setValue] = useState('[]'); + + return ( + setValue(JSON.stringify(items))} + /> + ); +}; + +export const withExistingValue = () => { + const [value, setValue] = useState(JSON.stringify([{ + key: 'One', + value: 'First record' + }, { + key: 'Two', + value: 'Second record' + }])); + + return ( + setValue(JSON.stringify(items))} + /> + ); +};