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

(website redesign) Feat(shellbox): migration #5234

Merged
merged 55 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
1ec7c30
feat(unit-test): introduce unit test on website redesign branch (#5178
AugustinMauroy Mar 23, 2023
1df6c68
feat(shellbox): introduce shellBox
AugustinMauroy Apr 5, 2023
5aca075
Update components/CommonComponents/ShellBox/index.module.scss
AugustinMauroy Apr 6, 2023
0e8441b
Update hooks/useCopyToClipboard.ts
AugustinMauroy Apr 6, 2023
3bf9eb6
feat(shellbox): update
AugustinMauroy Apr 6, 2023
0d9d5b6
chore: sass adon
AugustinMauroy Apr 8, 2023
ef6794f
feat(unit test): support I18N
AugustinMauroy Apr 9, 2023
4b5e672
fix lint
AugustinMauroy Apr 9, 2023
efba032
remove feat-unit test
AugustinMauroy Apr 10, 2023
ba2ea12
feat(shellbox): update with feedback
AugustinMauroy Apr 11, 2023
0f12d24
feat(shellbox): update name of test
AugustinMauroy Apr 11, 2023
1c210d6
Update hooks/useCopyToClipboard.ts
AugustinMauroy Apr 12, 2023
78df7e7
Update hooks/useCopyToClipboard.ts
AugustinMauroy Apr 12, 2023
aae068c
📎 chore(migration): migrate banner component (#5233)
manishprivet Apr 11, 2023
f6d5538
feat(shellbox): update with feedback
AugustinMauroy Apr 12, 2023
1dcbd5a
feat(ShellBox): fix unit test
AugustinMauroy Apr 15, 2023
f101035
fix: fails from rebase
AugustinMauroy Apr 15, 2023
c190801
feat(shellbox): update with feedback
AugustinMauroy Apr 15, 2023
d52080b
feat(shellbox): update with feedback
AugustinMauroy Apr 16, 2023
3a70e09
fix
AugustinMauroy Apr 17, 2023
f09de81
fix rebase
AugustinMauroy Apr 17, 2023
deaacc8
fix(unit test): jest env
AugustinMauroy Apr 17, 2023
eb7d596
fix build
AugustinMauroy Apr 17, 2023
86b9908
Merge branch 'major/website-redesign' into feat(shellbox)
shanpriyan Apr 18, 2023
b4d9e23
Update components/Common/ShellBox/index.tsx
AugustinMauroy Apr 18, 2023
d5f4fc4
update with review
AugustinMauroy Apr 19, 2023
c3d66e2
Merge branch 'feat(shellbox)' of https://github.com/AugustinMauroy/no…
AugustinMauroy Apr 19, 2023
cfd17dd
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 19, 2023
c04c221
fix
AugustinMauroy Apr 19, 2023
5622772
Merge branch 'feat(shellbox)' of https://github.com/AugustinMauroy/no…
AugustinMauroy Apr 19, 2023
6685456
Merge branch 'major/website-redesign' into feat(shellbox)
manishprivet Apr 20, 2023
9166780
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 20, 2023
d6d72fe
update
AugustinMauroy Apr 20, 2023
2c48a87
Merge branch 'feat(shellbox)' of https://github.com/AugustinMauroy/no…
AugustinMauroy Apr 20, 2023
ac3a56e
fix: fit with new contrib guideline
AugustinMauroy Apr 20, 2023
6b36262
feat(shellbox): simplify logic
AugustinMauroy Apr 20, 2023
455c087
Update components/Common/ShellBox/index.stories.tsx
AugustinMauroy Apr 20, 2023
e2f0fc3
Update index.stories.tsx
AugustinMauroy Apr 20, 2023
fb857df
fix
AugustinMauroy Apr 20, 2023
26c9909
Merge branch 'major/website-redesign' into feat(shellbox)
ovflowd Apr 20, 2023
d85404c
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 22, 2023
a91898b
update test with feedback
AugustinMauroy Apr 22, 2023
d1286f1
update unit test
AugustinMauroy Apr 23, 2023
d329e2d
Merge branch 'major/website-redesign' into feat(shellbox)
ovflowd Apr 24, 2023
7b70abc
Merge branch 'major/website-redesign' into feat(shellbox)
shanpriyan Apr 24, 2023
5b40d52
Merge branch 'major/website-redesign' into feat(shellbox)
manishprivet Apr 25, 2023
3040980
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 27, 2023
cc67076
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 27, 2023
dbf1eeb
update stories
AugustinMauroy Apr 27, 2023
a3dc746
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 30, 2023
d23c323
Merge branch 'major/website-redesign' into feat(shellbox)
AugustinMauroy Apr 30, 2023
b50466f
Update components/Common/ShellBox/index.tsx
AugustinMauroy May 1, 2023
9232761
update with feedback
AugustinMauroy May 1, 2023
d0f5721
Merge branch 'major/website-redesign' into feat(shellbox)
SEWeiTung May 2, 2023
f2d7f06
Merge branch 'major/website-redesign' into feat(shellbox)
mikeesto May 2, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ShellBox should render 1`] = `
<div>
<pre
class="shellBox"
>
<div
class="top"
>
<span>
SHELL
</span>
<button
type="button"
>
components.common.shellBox.copy
</button>
</div>
<code>
test
</code>
</pre>
</div>
`;
58 changes: 58 additions & 0 deletions components/Common/ShellBox/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from 'react-intl';
import ShellBox from '../index';

const mockWriteText = jest.fn();
const originalNavigator = { ...window.navigator };

describe('ShellBox', () => {
beforeEach(() => {
Object.defineProperty(window, 'navigator', {
value: {
clipboard: {
writeText: mockWriteText,
},
},
});
});

afterEach(() => {
Object.defineProperty(window, 'navigator', {
value: originalNavigator,
});
});

it('should render', () => {
const { container } = render(
<IntlProvider locale="en" onError={() => {}}>
<ShellBox>test</ShellBox>
</IntlProvider>
);
expect(container).toMatchSnapshot();
});

it('should call clipboard API with `test` once', async () => {
const user = userEvent.setup();
const navigatorClipboardWriteTextSpy = jest
.fn()
.mockImplementation(() => Promise.resolve());

Object.defineProperty(window.navigator, 'clipboard', {
writable: true,
value: {
writeText: navigatorClipboardWriteTextSpy,
},
});

render(
<IntlProvider locale="en" onError={() => {}}>
<ShellBox textToCopy="test">test</ShellBox>
</IntlProvider>
);
const button = screen.getByRole('button');
await user.click(button);
expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledTimes(1);
expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledWith('test');
});
});
75 changes: 75 additions & 0 deletions components/Common/ShellBox/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.shellBox {
background-color: var(--black10);
border-radius: 0.4rem;
box-sizing: border-box;
display: flex;
flex-direction: column;
font-family: var(--mono);
padding: 0 0 var(--space-48) var(--space-16);
position: relative;

code {
color: var(--pink5);
font-family: inherit;
line-height: 30px;
overflow-x: hidden;
position: absolute;
top: 30px;
width: calc(100% - 20px);

&:hover {
overflow-x: auto;
}

&::-webkit-scrollbar {
height: 0.5em;
}

&::-webkit-scrollbar,
&::-webkit-scrollbar-thumb {
border-radius: 4px;
overflow: visible;
}

&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
}

> span.function {
color: var(--warning5);
}
}

.top {
display: inherit;
flex-direction: row;
justify-content: space-between;
margin-bottom: var(--space-08);

span,
button {
align-items: center;
display: inherit;
font-size: var(--font-size-code);
height: 23px;
justify-content: center;
width: 86px;
}

span {
background-color: var(--black3);
border-radius: 0 0 0.3rem 0.3rem;
color: var(--black9);
margin-left: 1.6rem;
}

button {
background-color: var(--brand);
border-radius: 0 0.3rem 0.3rem 0.3rem;
border-width: 0;
i {
padding: 0;
}
}
}
}
31 changes: 31 additions & 0 deletions components/Common/ShellBox/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import ShellBox from './index';
import type { Meta as MetaObj, StoryObj } from '@storybook/react';

type Story = StoryObj<typeof ShellBox>;
type Meta = MetaObj<typeof ShellBox>;

export const Default: Story = {
args: {
children: 'echo hello world',
textToCopy: 'echo hello world',
},
};

export const WithoutTextToCopy: Story = {
args: {
children: 'echo hello world',
},
};

export const WithTextToCopyJsx: Story = {
args: {
children: (
<span>
<strong>$</strong>echo hello world
</span>
),
textToCopy: 'echo hello world',
},
};

export default { component: ShellBox } as Meta;
45 changes: 45 additions & 0 deletions components/Common/ShellBox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useRef } from 'react';
import { FormattedMessage } from 'react-intl';
import styles from './index.module.scss';
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
import type { FC, PropsWithChildren, MouseEvent, ReactNode } from 'react';

type ShellBoxProps = {
children: string | ReactNode;
textToCopy?: string;
};

const ShellBox: FC<PropsWithChildren<ShellBoxProps>> = ({
children,
textToCopy,
}: PropsWithChildren<ShellBoxProps>) => {
const [copied, copyText] = useCopyToClipboard();

const shellBoxRef = useRef<HTMLElement>(null);

const handleCopyCode = async (event: MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
// By default we want to use the textToCopy props but if it's absent
// we allow the user to copy by getting the inner HTML content of the Element
const _textToCopy = textToCopy || shellBoxRef.current?.innerHTML || '';

await copyText(_textToCopy.replace('$', ''));
};

return (
<pre className={styles.shellBox}>
<div className={styles.top}>
<span>SHELL</span>
<button type="button" onClick={handleCopyCode}>
<FormattedMessage
id="components.common.shellBox.copy"
values={{ copied }}
/>
</button>
</div>
<code ref={shellBoxRef}>{children}</code>
</pre>
);
};

export default ShellBox;
1 change: 1 addition & 0 deletions components/Common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ShellBox } from './ShellBox';
59 changes: 59 additions & 0 deletions hooks/__test__/useCopyToClipboard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { render, fireEvent, screen } from '@testing-library/react';
import { FormattedMessage } from 'react-intl';
import { IntlProvider } from 'react-intl';
import { useCopyToClipboard } from '../useCopyToClipboard';

const mockWriteText = jest.fn();
const originalNavigator = { ...window.navigator };

describe('useCopyToClipboard', () => {
beforeEach(() => {
Object.defineProperty(window, 'navigator', {
value: {
clipboard: {
writeText: mockWriteText,
},
},
});
});

afterEach(() => {
Object.defineProperty(window, 'navigator', {
value: originalNavigator,
});
});

const TestComponent = ({ textToCopy }: { textToCopy: string }) => {
const [copied, copyText] = useCopyToClipboard();

return (
<IntlProvider locale="en" onError={() => {}}>
<button onClick={() => copyText(textToCopy)} type="button">
<FormattedMessage
id="components.common.shellBox.copy"
values={{ copied }}
/>
</button>
</IntlProvider>
);
};

it('should call clipboard API with `test` once', () => {
const navigatorClipboardWriteTextSpy = jest
.fn()
.mockImplementation(() => Promise.resolve());

Object.defineProperty(window.navigator, 'clipboard', {
writable: true,
value: {
writeText: navigatorClipboardWriteTextSpy,
},
});

render(<TestComponent textToCopy="test" />);
const button = screen.getByRole('button');
fireEvent.click(button);
expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledTimes(1);
expect(navigatorClipboardWriteTextSpy).toHaveBeenCalledWith('test');
});
});
31 changes: 31 additions & 0 deletions hooks/useCopyToClipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect, useState } from 'react';

const copyToClipboard = (value: string | undefined) => {
if (!value || typeof navigator === 'undefined') {
return Promise.resolve(false);
}

return navigator.clipboard
.writeText(value)
.then(() => true)
.catch(() => false);
};

export const useCopyToClipboard = () => {
const [copied, setCopied] = useState(false);

const copyText = (text: string | undefined) =>
copyToClipboard(text).then(setCopied);

useEffect(() => {
if (copied) {
const timerId = setTimeout(() => setCopied(false), 3000);

return () => clearTimeout(timerId);
}

return undefined;
}, [copied]);

return [copied, copyText] as const;
};
3 changes: 2 additions & 1 deletion i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@
"pages.index.features.openSource.title": "Open Source",
"pages.index.features.openSource.description": "Node.js is open source and actively maintained by contributors all over the world",
"pages.index.features.everywhere.title": "Everywhere",
"pages.index.features.everywhere.description": "Node.js has been adapted to work in a wide variety of places"
"pages.index.features.everywhere.description": "Node.js has been adapted to work in a wide variety of places",
"components.common.shellBox.copy": "{copied, select, true {copied}other {copy}}"
}