Skip to content

Commit

Permalink
feat: Adds EditableText component
Browse files Browse the repository at this point in the history
  • Loading branch information
amir-zahedi committed Oct 13, 2023
1 parent c9beb90 commit ed898a2
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/eleven-doors-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@autoguru/overdrive': minor
---

Adds EditableText component
16 changes: 16 additions & 0 deletions packages/overdrive/lib/components/EditableText/EditableText.css.ts
Original file line number Diff line number Diff line change
@@ -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',
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { render } from '@testing-library/react';
import * as React from 'react';

import { EditableText } from './EditableText';


describe('<EditableText />', () => {
it('should not throw', () =>
expect(() => render(<EditableText />)).not.toThrow());

it('should match snapshot for default bullet text', () => {
expect(
render(<EditableText type='text' colour='primary' value='Hello World!' />).container.firstChild,
).toMatchSnapshot();
});

it('should match snapshot with custom size', () => {
expect(
render(
<EditableText type='text' size='5' value='Hello World!' />,
).container.firstChild,
).toMatchSnapshot();
});

it('should match snapshot as date input', () => {
expect(
render(
<EditableText type='date' colour='primary' value='Hello World!' />,
).container.firstChild,
).toMatchSnapshot();
});

});
84 changes: 84 additions & 0 deletions packages/overdrive/lib/components/EditableText/EditableText.tsx
Original file line number Diff line number Diff line change
@@ -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<ComponentProps<typeof Text>, 'display'>;
type TextProps = Pick<ComponentProps<typeof Text>, 'is' | 'colour' | 'size' | 'display' | 'children' | 'noWrap'>;
type InputProps = Omit<
InputHTMLAttributes<HTMLInputElement>,
'style' | 'is' | 'autoFocus' | 'width' | 'height' | keyof TextProps | keyof BoxProps
>;

export interface Props extends TextProps, InputProps, BoxProps {
className?: string;
}

export const EditableText = forwardRef<HTMLAnchorElement, Props>(
(
{
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 (
<Box ref={ref}
display={display}
className={styles.root}
style={{ maxWidth:width }}
onClick={onRequestEdit}
onFocus={onRequestEdit}
onBlur={() => setIsEditing(false)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === 'Escape') {
setIsEditing(false);
}
}}
>
{isEditing ? (
<Box
is="input"
{...inputProps}
autoFocus
value={value}
className={clsx(
textStyles,
inputStyles.input.itself.root,
)}
style={{ width }}
/>
) : (
<Text
noWrap
is={is}
colour={colour}
className={clsx(textStyles, styles.text)}
style={{ maxWidth:width }}
>
{value}
</Text>
)}
</Box>

);
},
);

export default EditableText;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<EditableText /> should match snapshot as date input 1`] = `
<div
class="reset_block__6swfds3 useBoxStyles_display_inlineBlock__7o6k06fe EditableText_root__n0hvwi0"
style="max-width: 12ch;"
>
<span
class="reset_inlineText__6swfds2 useBoxStyles_textAlign_left__7o6k06f2 useTextStyles_root__w6to7i0 reset_inlineText__6swfds2 useTextStyles_colours_primary__w6to7ia useTextStyles_fontWeight_normal__w6to7in useTextStyles_noWrap__w6to7iq useTextStyles_sizes_4__w6to7i4 useTextStyles_root__w6to7i0 reset_block__6swfds3 useTextStyles_colours_primary__w6to7ia EditableText_text__n0hvwi1"
style="max-width: 12ch;"
>
Hello World!
</span>
</div>
`;

exports[`<EditableText /> should match snapshot for default bullet text 1`] = `
<div
class="reset_block__6swfds3 useBoxStyles_display_inlineBlock__7o6k06fe EditableText_root__n0hvwi0"
style="max-width: 12ch;"
>
<span
class="reset_inlineText__6swfds2 useBoxStyles_textAlign_left__7o6k06f2 useTextStyles_root__w6to7i0 reset_inlineText__6swfds2 useTextStyles_colours_primary__w6to7ia useTextStyles_fontWeight_normal__w6to7in useTextStyles_noWrap__w6to7iq useTextStyles_sizes_4__w6to7i4 useTextStyles_root__w6to7i0 reset_block__6swfds3 useTextStyles_colours_primary__w6to7ia EditableText_text__n0hvwi1"
style="max-width: 12ch;"
>
Hello World!
</span>
</div>
`;

exports[`<EditableText /> should match snapshot with custom size 1`] = `
<div
class="reset_block__6swfds3 useBoxStyles_display_inlineBlock__7o6k06fe EditableText_root__n0hvwi0"
style="max-width: 12ch;"
>
<span
class="reset_inlineText__6swfds2 useBoxStyles_textAlign_left__7o6k06f2 useTextStyles_root__w6to7i0 reset_inlineText__6swfds2 useTextStyles_colours_muted__w6to7ig useTextStyles_fontWeight_normal__w6to7in useTextStyles_noWrap__w6to7iq useTextStyles_sizes_4__w6to7i4 useTextStyles_root__w6to7i0 reset_block__6swfds3 useTextStyles_colours_muted__w6to7ig useTextStyles_sizes_5__w6to7i5 EditableText_text__n0hvwi1"
style="max-width: 12ch;"
>
Hello World!
</span>
</div>
`;
1 change: 1 addition & 0 deletions packages/overdrive/lib/components/EditableText/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EditableText } from './EditableText';
62 changes: 62 additions & 0 deletions packages/overdrive/lib/components/EditableText/stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof EditableText>;

const template: ComponentStory<typeof EditableText> = (args) => (
<EditableText {...args} />
);

const textProps: ComponentProps<typeof EditableText> = {
colour: 'muted',
value: 'Hello World',
type: 'text',
};
const numberProps: ComponentProps<typeof EditableText> = {
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<typeof EditableText> = {
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;
5 changes: 4 additions & 1 deletion packages/overdrive/lib/components/Text/Text.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -15,6 +15,7 @@ export interface Props extends TextStyleProps {
BoxStyleProps['display'],
'inline' | 'inlineBlock' | 'block'
>;
style?: CSSProperties;
}

export const Text: FunctionComponent<Props> = ({
Expand All @@ -30,6 +31,7 @@ export const Text: FunctionComponent<Props> = ({
noWrap,
size = '4',
strong = false,
style,
}) => (
<Box
is={Component}
Expand All @@ -47,6 +49,7 @@ export const Text: FunctionComponent<Props> = ({
}),
className,
]}
style={style}
>
{children}
</Box>
Expand Down

0 comments on commit ed898a2

Please sign in to comment.