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: Adds EditableText component #927

Merged
merged 1 commit into from
Oct 13, 2023
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
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
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