From ed898a22223b3506a9a5b17511a8bdb39661f903 Mon Sep 17 00:00:00 2001 From: Amir Zahedi Date: Fri, 13 Oct 2023 08:03:53 +0800 Subject: [PATCH] feat: Adds EditableText component --- .changeset/eleven-doors-watch.md | 5 ++ .../EditableText/EditableText.css.ts | 16 ++++ .../EditableText/EditableText.spec.jsx | 33 ++++++++ .../components/EditableText/EditableText.tsx | 84 +++++++++++++++++++ .../__snapshots__/EditableText.spec.jsx.snap | 43 ++++++++++ .../lib/components/EditableText/index.ts | 1 + .../lib/components/EditableText/stories.tsx | 62 ++++++++++++++ .../overdrive/lib/components/Text/Text.tsx | 5 +- 8 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 .changeset/eleven-doors-watch.md create mode 100644 packages/overdrive/lib/components/EditableText/EditableText.css.ts create mode 100644 packages/overdrive/lib/components/EditableText/EditableText.spec.jsx create mode 100644 packages/overdrive/lib/components/EditableText/EditableText.tsx create mode 100644 packages/overdrive/lib/components/EditableText/__snapshots__/EditableText.spec.jsx.snap create mode 100644 packages/overdrive/lib/components/EditableText/index.ts create mode 100644 packages/overdrive/lib/components/EditableText/stories.tsx diff --git a/.changeset/eleven-doors-watch.md b/.changeset/eleven-doors-watch.md new file mode 100644 index 000000000..a40bbd1c3 --- /dev/null +++ b/.changeset/eleven-doors-watch.md @@ -0,0 +1,5 @@ +--- +'@autoguru/overdrive': minor +--- + +Adds EditableText component diff --git a/packages/overdrive/lib/components/EditableText/EditableText.css.ts b/packages/overdrive/lib/components/EditableText/EditableText.css.ts new file mode 100644 index 000000000..b99c235e8 --- /dev/null +++ b/packages/overdrive/lib/components/EditableText/EditableText.css.ts @@ -0,0 +1,16 @@ +import { style } from '@vanilla-extract/css'; + +import { themeContractVars as vars } from '../../themes/theme.css'; + +export const root = style({ + transitionDelay: '0s', + transitionTimingFunction: 'cubic-bezier(0, 0, 0.2, 1)', + transitionDuration: '0.2s', + transitionProperty: 'box-shadow', + boxShadow: `inset 0 -1px 0 0 ${vars.typography.colour.muted}`, +}); + +export const text = style({ + cursor: 'pointer', +}); + diff --git a/packages/overdrive/lib/components/EditableText/EditableText.spec.jsx b/packages/overdrive/lib/components/EditableText/EditableText.spec.jsx new file mode 100644 index 000000000..71c49f961 --- /dev/null +++ b/packages/overdrive/lib/components/EditableText/EditableText.spec.jsx @@ -0,0 +1,33 @@ +import { render } from '@testing-library/react'; +import * as React from 'react'; + +import { EditableText } from './EditableText'; + + +describe('', () => { + it('should not throw', () => + expect(() => render()).not.toThrow()); + + it('should match snapshot for default bullet text', () => { + expect( + render().container.firstChild, + ).toMatchSnapshot(); + }); + + it('should match snapshot with custom size', () => { + expect( + render( + , + ).container.firstChild, + ).toMatchSnapshot(); + }); + + it('should match snapshot as date input', () => { + expect( + render( + , + ).container.firstChild, + ).toMatchSnapshot(); + }); + +}); diff --git a/packages/overdrive/lib/components/EditableText/EditableText.tsx b/packages/overdrive/lib/components/EditableText/EditableText.tsx new file mode 100644 index 000000000..ac2251a31 --- /dev/null +++ b/packages/overdrive/lib/components/EditableText/EditableText.tsx @@ -0,0 +1,84 @@ +import clsx from 'clsx'; +import * as React from 'react'; +import { ComponentProps, forwardRef, InputHTMLAttributes, useState } from 'react'; + +import { Box } from '../Box'; +import { Text, useTextStyles } from '../Text'; +import * as inputStyles from '../private/InputBase/withEnhancedInput.css'; + +import * as styles from './EditableText.css'; +type BoxProps = Pick, 'display'>; +type TextProps = Pick, 'is' | 'colour' | 'size' | 'display' | 'children' | 'noWrap'>; +type InputProps = Omit< + InputHTMLAttributes, + 'style' | 'is' | 'autoFocus' | 'width' | 'height' | keyof TextProps | keyof BoxProps +>; + +export interface Props extends TextProps, InputProps, BoxProps { + className?: string; +} + +export const EditableText = forwardRef( + ( + { + is, + colour = 'muted', + size, + display = 'inlineBlock', + value, + ...inputProps + }, + ref, + ) => { + const [isEditing, setIsEditing] = useState(false); + const onRequestEdit = () => setIsEditing(true); + const textStyles = useTextStyles({ + is, + colour, + size, + }); + const width = value?`${value.toString().length}ch`:void 0; + return ( + setIsEditing(false)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === 'Escape') { + setIsEditing(false); + } + }} + > + {isEditing ? ( + + ) : ( + + {value} + + )} + + + ); + }, +); + +export default EditableText; diff --git a/packages/overdrive/lib/components/EditableText/__snapshots__/EditableText.spec.jsx.snap b/packages/overdrive/lib/components/EditableText/__snapshots__/EditableText.spec.jsx.snap new file mode 100644 index 000000000..ea4adaa9d --- /dev/null +++ b/packages/overdrive/lib/components/EditableText/__snapshots__/EditableText.spec.jsx.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match snapshot as date input 1`] = ` +
+ + Hello World! + +
+`; + +exports[` should match snapshot for default bullet text 1`] = ` +
+ + Hello World! + +
+`; + +exports[` should match snapshot with custom size 1`] = ` +
+ + Hello World! + +
+`; diff --git a/packages/overdrive/lib/components/EditableText/index.ts b/packages/overdrive/lib/components/EditableText/index.ts new file mode 100644 index 000000000..684c58fbd --- /dev/null +++ b/packages/overdrive/lib/components/EditableText/index.ts @@ -0,0 +1 @@ +export { EditableText } from './EditableText'; diff --git a/packages/overdrive/lib/components/EditableText/stories.tsx b/packages/overdrive/lib/components/EditableText/stories.tsx new file mode 100644 index 000000000..97af01f61 --- /dev/null +++ b/packages/overdrive/lib/components/EditableText/stories.tsx @@ -0,0 +1,62 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import isChromatic from 'chromatic'; +import * as React from 'react'; +import { ComponentProps } from 'react'; + +import { EditableText } from '.'; + +export default { + title: 'Components/Inputs/EditableText', + component: EditableText, + argTypes: { + colour: { + options: ['muted', 'primary', 'secondary'], + defaultValue: 'primary', + control: { + type: 'select', + }, + }, + }, +} as ComponentMeta; + +const template: ComponentStory = (args) => ( + +); + +const textProps: ComponentProps = { + colour: 'muted', + value: 'Hello World', + type: 'text', +}; +const numberProps: ComponentProps = { + colour: 'muted', + value: '20', + type: 'number', +}; + + +const formatDate = (date: Date = new Date()) => { + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, '0'); + + return `${year}-${month}-${day}`; +}; + +const todayStr: string = formatDate( + isChromatic() ? new Date(2019, 5, 1) : new Date(), +); +const dateProps: ComponentProps = { + colour: 'muted', + value: todayStr, + type:'date' +}; + + +export const text = template.bind(textProps); +export const number = template.bind(numberProps); +export const date = template.bind(dateProps); + +text.args = textProps; +number.args = numberProps; +date.args = dateProps; diff --git a/packages/overdrive/lib/components/Text/Text.tsx b/packages/overdrive/lib/components/Text/Text.tsx index d63f48efe..094b2b38e 100644 --- a/packages/overdrive/lib/components/Text/Text.tsx +++ b/packages/overdrive/lib/components/Text/Text.tsx @@ -1,4 +1,4 @@ -import type { FunctionComponent, ReactNode } from 'react'; +import type { FunctionComponent, ReactNode, CSSProperties } from 'react'; import * as React from 'react'; import type { BoxStyleProps } from '../Box'; @@ -15,6 +15,7 @@ export interface Props extends TextStyleProps { BoxStyleProps['display'], 'inline' | 'inlineBlock' | 'block' >; + style?: CSSProperties; } export const Text: FunctionComponent = ({ @@ -30,6 +31,7 @@ export const Text: FunctionComponent = ({ noWrap, size = '4', strong = false, + style, }) => ( = ({ }), className, ]} + style={style} > {children}