Skip to content

Commit

Permalink
chore: added core functionality, ready for further styling
Browse files Browse the repository at this point in the history
  • Loading branch information
tjmaynes committed Jun 24, 2024
1 parent 9441230 commit 7810b3c
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 53 deletions.
102 changes: 70 additions & 32 deletions e2e/home.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test'
import { test, expect, Locator } from '@playwright/test'
import { getJsonExamples } from './fixtures'

test.describe('when a user navigates to the homepage', () => {
Expand Down Expand Up @@ -29,29 +29,65 @@ test.describe('when a user navigates to the homepage', () => {
})
})

test('should copy json to clipboard when copy button clicked', async ({
page,
browserName,
}) => {
test.describe('other functionality', () => {
const expected = '[{"hello" : "world"}, {"green": "red"}]'
let placeholderText: Locator

await page.getByLabel('Type or paste your json here...').fill(expected)
test.beforeEach(async ({ page }) => {
placeholderText = page.getByPlaceholder('Type or paste your json here...')

await page.getByLabel('Copy').click()
await placeholderText.fill(expected)
})

if (!['webkit', 'Desktop Safari', 'Mobile Safari'].includes(browserName)) {
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
)
const clipboardContent = await handle.jsonValue()
expect(clipboardContent).toEqual(expected)
}
})
test('should copy json to clipboard when copy button clicked', async ({
page,
browserName,
}) => {
await page.getByLabel('Copy').click()

if (
!['webkit', 'Desktop Safari', 'Mobile Safari'].includes(browserName)
) {
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
)
const clipboardContent = await handle.jsonValue()
expect(clipboardContent).toEqual(expected)
}

await expect(page.getByText('Copied to clipboard!')).toBeVisible()
})

test('should compress json when compress button clicked', async ({
page,
}) => {
await expect(placeholderText.getByText(expected)).toBeVisible()

await page.getByLabel('Compress').click()

await expect(
placeholderText.getByText('[{"hello":"world"},{"green":"red"}]')
).toBeVisible()

await expect(page.getByText('Compressed')).toBeVisible()
})

test('should clear textarea when clear button clicked', async ({
page,
}) => {
await expect(placeholderText.getByText(expected)).toBeVisible()

test('should prettify unpretty json input when pretty button clicked and input is valid', async ({
page,
}) => {
const expectedOutput = `[
await page.getByLabel('Clear').click()

await expect(placeholderText.getByText(expected)).not.toBeVisible()

await expect(page.getByText('Cleared')).toBeVisible()
})

test('should prettify unpretty json input when pretty button clicked and input is valid', async ({
page,
}) => {
const expectedOutput = `[
{
"hello": "world"
},
Expand All @@ -60,21 +96,23 @@ test.describe('when a user navigates to the homepage', () => {
}
]`

await page
.getByLabel('Type or paste your json here...')
.fill('[ {"hello" : "world"}, { "green": "red"}]')
await page
.getByLabel('Type or paste your json here...')
.fill('[ {"hello" : "world"}, { "green": "red"}]')

await expect(page.getByText('👍')).toBeVisible()
await expect(page.getByText('👍')).toBeVisible()

await page.getByLabel('Pretty').click()
await page.getByLabel('Pretty').click()

await expect(
page
.getByPlaceholder('Type or paste your json here...')
.getByText(expectedOutput)
).toBeVisible()
await expect(page.getByText('👍')).toBeVisible()
})
await expect(
page
.getByPlaceholder('Type or paste your json here...')
.getByText(expectedOutput)
).toBeVisible()

await expect(page.getByText('👍')).toBeVisible()
})

test('pretty button is disabled when input is invalid ', () => {})
test('pretty button is disabled when input is invalid ', () => {})
})
})
29 changes: 26 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"dependencies": {
"@uiw/react-textarea-code-editor": "^3.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1"
},
"devDependencies": {
"@playwright/test": "^1.44.1",
Expand Down
80 changes: 66 additions & 14 deletions src/components/SimpleJsonEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useReducer } from 'react'
import { JsonEditor } from '@/components/JsonEditor.tsx'
import { isValidJson, prettifyJson } from '@/utils/json-utils.ts'
import { compressJson, isValidJson, prettifyJson } from '@/utils/json-utils.ts'
import toast, { Toaster } from 'react-hot-toast'

enum SimpleJsonEditorStates {
INITIAL,
Expand All @@ -26,7 +27,9 @@ type SimpleJsonEditorState =

enum SimpleJsonEditorActions {
ON_TYPING,
ON_FORMAT_BUTTON_CLICK,
ON_PRETTY_BUTTON_CLICK,
ON_COMPRESS_BUTTON_CLICK,
ON_CLEAR_BUTTON_CLICK,
}

type SimpleJsonEditorAction =
Expand All @@ -35,12 +38,17 @@ type SimpleJsonEditorAction =
entry: string
}
| {
action: SimpleJsonEditorActions.ON_FORMAT_BUTTON_CLICK
value: string
action: SimpleJsonEditorActions.ON_PRETTY_BUTTON_CLICK
}
| {
action: SimpleJsonEditorActions.ON_COMPRESS_BUTTON_CLICK
}
| {
action: SimpleJsonEditorActions.ON_CLEAR_BUTTON_CLICK
}

const simpleJsonEditorReducer = (
_: SimpleJsonEditorState,
state: SimpleJsonEditorState,
action: SimpleJsonEditorAction
): SimpleJsonEditorState => {
switch (action.action) {
Expand All @@ -60,10 +68,20 @@ const simpleJsonEditorReducer = (
value: action.entry,
}
}
case SimpleJsonEditorActions.ON_FORMAT_BUTTON_CLICK:
case SimpleJsonEditorActions.ON_PRETTY_BUTTON_CLICK:
return {
state: SimpleJsonEditorStates.VALID,
value: prettifyJson(state.value),
}
case SimpleJsonEditorActions.ON_COMPRESS_BUTTON_CLICK:
return {
state: SimpleJsonEditorStates.VALID,
value: prettifyJson(action.value),
value: compressJson(state.value),
}
case SimpleJsonEditorActions.ON_CLEAR_BUTTON_CLICK:
return {
state: SimpleJsonEditorStates.INITIAL,
value: '',
}
}
}
Expand All @@ -89,12 +107,36 @@ export const SimpleJsonEditor = () => {

const { color } = getPresentationStyle(state)

const onPrettyButtonClickedHandler = useCallback(() => {
dispatch({
action: SimpleJsonEditorActions.ON_PRETTY_BUTTON_CLICK,
})

toast('Prettified')
}, [])

const onCompressButtonClickedHandler = useCallback(() => {
dispatch({
action: SimpleJsonEditorActions.ON_COMPRESS_BUTTON_CLICK,
})

toast('Compressed')
}, [])

const onCopyButtonClickedHandler = useCallback(() => {
navigator.clipboard.writeText(state.value)

// TODO: show toast?
toast.success('Copied to clipboard!')
}, [state])

const onClearButtonClickedHandler = useCallback(() => {
dispatch({
action: SimpleJsonEditorActions.ON_CLEAR_BUTTON_CLICK,
})

toast('Cleared')
}, [])

return (
<div className="flex flex-col w-full">
<JsonEditor
Expand All @@ -113,25 +155,35 @@ export const SimpleJsonEditor = () => {
<button
aria-label="Pretty"
disabled={state.state !== SimpleJsonEditorStates.VALID}
onClick={() =>
dispatch({
action: SimpleJsonEditorActions.ON_FORMAT_BUTTON_CLICK,
value: state.value,
})
}
onClick={() => onPrettyButtonClickedHandler()}
>
Pretty
</button>
<button
aria-label="Compress"
disabled={state.state !== SimpleJsonEditorStates.VALID}
onClick={() => onCompressButtonClickedHandler()}
>
Compress
</button>
<button
aria-label="Copy"
disabled={state.state !== SimpleJsonEditorStates.VALID}
onClick={() => onCopyButtonClickedHandler()}
>
Copy
</button>
<button
aria-label="Clear"
disabled={state.state !== SimpleJsonEditorStates.VALID}
onClick={() => onClearButtonClickedHandler()}
>
Clear
</button>
<ValidationStatus {...state} />
</div>
)}
<Toaster position="top-right" reverseOrder={false} />
</div>
)
}
Expand Down
8 changes: 5 additions & 3 deletions src/utils/json-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const isValidJson = (rawValue: string): boolean => {
}
}

export const prettifyJson = (rawValue: string): string => {
return JSON.stringify(JSON.parse(rawValue), null, ' ')
}
export const prettifyJson = (rawValue: string): string =>
JSON.stringify(JSON.parse(rawValue), null, ' ')

export const compressJson = (rawValue: string): string =>
JSON.stringify(JSON.parse(rawValue), null, '')

0 comments on commit 7810b3c

Please sign in to comment.