[web] Colour picker for tags (#12255)
* Base for color picker * React color picker and updated modals * Add tag color picker to mobile dashboard * Update existing tests and fix disable save button condition * CSS adaptations for desktop modal streched into mobile display * Update TagsController tests * Add aria-hidden label on color pickers * Fix linting * Fix project list test * Select random color when creating tag * Cleanup leftovers in project list context * Test cleanup * Pre-select custom color and store local color while picking * Add type to preset colors * Add css fix to override disabled button opacity * Skip redundant check * Fix linting * Add back btn-secondary on manage tag modal after rebase GitOrigin-RevId: a4cf24e85cc0ca01466f4bf9c77482be8360e68e
This commit is contained in:
committed by
Copybot
parent
fb6746a887
commit
04c204f989
+4
-4
@@ -799,9 +799,9 @@ describe('<ProjectListRoot />', function () {
|
||||
).findByText<HTMLElement>('More')
|
||||
fireEvent.click(moreDropdown)
|
||||
|
||||
const renameButton =
|
||||
screen.getAllByText<HTMLButtonElement>('Rename')[1] // first one is for the tag in the sidebar
|
||||
fireEvent.click(renameButton)
|
||||
const editButton =
|
||||
screen.getAllByText<HTMLButtonElement>('Rename')[0]
|
||||
fireEvent.click(editButton)
|
||||
|
||||
const modals = await screen.findAllByRole('dialog')
|
||||
const modal = modals[0]
|
||||
@@ -837,7 +837,7 @@ describe('<ProjectListRoot />', function () {
|
||||
fireEvent.click(moreDropdown)
|
||||
|
||||
const renameButton =
|
||||
within(actionsToolbar).getByText<HTMLButtonElement>('Rename') // first one is for the tag in the sidebar
|
||||
within(actionsToolbar).getByText<HTMLButtonElement>('Rename')
|
||||
fireEvent.click(renameButton)
|
||||
|
||||
const modals = await screen.findAllByRole('dialog')
|
||||
|
||||
+30
-24
@@ -28,7 +28,7 @@ describe('<TagsList />', function () {
|
||||
project_ids: [],
|
||||
})
|
||||
fetchMock.post('express:/tag/:tagId/projects', 200)
|
||||
fetchMock.post('express:/tag/:tagId/rename', 200)
|
||||
fetchMock.post('express:/tag/:tagId/edit', 200)
|
||||
fetchMock.delete('express:/tag/:tagId', 200)
|
||||
|
||||
renderWithProjectListContext(<TagsList />)
|
||||
@@ -79,7 +79,7 @@ describe('<TagsList />', function () {
|
||||
)
|
||||
})
|
||||
|
||||
describe('create modal', function () {
|
||||
describe('Create modal', function () {
|
||||
beforeEach(async function () {
|
||||
const newTagButton = screen.getByRole('button', {
|
||||
name: 'New Folder',
|
||||
@@ -156,22 +156,22 @@ describe('<TagsList />', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('rename modal', function () {
|
||||
describe('Edit modal', function () {
|
||||
beforeEach(async function () {
|
||||
const tag1Button = screen.getByText('Tag 1')
|
||||
|
||||
const renameButton = within(
|
||||
const editButton = within(
|
||||
tag1Button.closest('li') as HTMLElement
|
||||
).getByRole('button', {
|
||||
name: 'Rename',
|
||||
name: 'Edit',
|
||||
})
|
||||
|
||||
await fireEvent.click(renameButton)
|
||||
await fireEvent.click(editButton)
|
||||
})
|
||||
|
||||
it('modal is open', async function () {
|
||||
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
|
||||
within(modal).getByRole('heading', { name: 'Rename Folder' })
|
||||
within(modal).getByRole('heading', { name: 'Edit Folder' })
|
||||
})
|
||||
|
||||
it('click on cancel closes the modal', async function () {
|
||||
@@ -182,14 +182,20 @@ describe('<TagsList />', function () {
|
||||
expect(screen.queryByRole('dialog', { hidden: false })).to.be.null
|
||||
})
|
||||
|
||||
it('Rename button is disabled when input is empty', async function () {
|
||||
it('Save button is disabled when input is empty', async function () {
|
||||
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
|
||||
const renameButton = within(modal).getByRole('button', { name: 'Rename' })
|
||||
const input = within(modal).getByRole('textbox')
|
||||
fireEvent.change(input, {
|
||||
target: {
|
||||
value: '',
|
||||
},
|
||||
})
|
||||
const saveButton = within(modal).getByRole('button', { name: 'Save' })
|
||||
|
||||
expect(renameButton.hasAttribute('disabled')).to.be.true
|
||||
expect(saveButton.hasAttribute('disabled')).to.be.true
|
||||
})
|
||||
|
||||
it('Rename button is disabled with error message when tag name is too long', async function () {
|
||||
it('Save button is disabled with error message when tag name is too long', async function () {
|
||||
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
|
||||
const input = within(modal).getByRole('textbox')
|
||||
fireEvent.change(input, {
|
||||
@@ -198,18 +204,18 @@ describe('<TagsList />', function () {
|
||||
},
|
||||
})
|
||||
|
||||
const createButton = within(modal).getByRole('button', { name: 'Rename' })
|
||||
expect(createButton.hasAttribute('disabled')).to.be.true
|
||||
const saveButton = within(modal).getByRole('button', { name: 'Save' })
|
||||
expect(saveButton.hasAttribute('disabled')).to.be.true
|
||||
screen.getByText('Tag name cannot exceed 50 characters')
|
||||
})
|
||||
|
||||
it('Rename button is disabled with no error message when tag name is unchanged', async function () {
|
||||
it('Save button is disabled with no error message when tag name is unchanged', async function () {
|
||||
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
|
||||
const createButton = within(modal).getByRole('button', { name: 'Rename' })
|
||||
expect(createButton.hasAttribute('disabled')).to.be.true
|
||||
const saveButton = within(modal).getByRole('button', { name: 'Save' })
|
||||
expect(saveButton.hasAttribute('disabled')).to.be.true
|
||||
})
|
||||
|
||||
it('Rename button is disabled with error message when tag name is already used', async function () {
|
||||
it('Save button is disabled with error message when tag name is already used', async function () {
|
||||
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
|
||||
const input = within(modal).getByRole('textbox')
|
||||
fireEvent.change(input, {
|
||||
@@ -218,20 +224,20 @@ describe('<TagsList />', function () {
|
||||
},
|
||||
})
|
||||
|
||||
const createButton = within(modal).getByRole('button', { name: 'Rename' })
|
||||
expect(createButton.hasAttribute('disabled')).to.be.true
|
||||
const saveButton = within(modal).getByRole('button', { name: 'Save' })
|
||||
expect(saveButton.hasAttribute('disabled')).to.be.true
|
||||
screen.getByText('Tag "Another tag" already exists')
|
||||
})
|
||||
|
||||
it('filling the input and clicking Rename sends a request', async function () {
|
||||
it('filling the input and clicking Save sends a request', async function () {
|
||||
const modal = screen.getAllByRole('dialog', { hidden: false })[0]
|
||||
const input = within(modal).getByRole('textbox')
|
||||
fireEvent.change(input, { target: { value: 'New Tag Name' } })
|
||||
|
||||
const renameButton = within(modal).getByRole('button', { name: 'Rename' })
|
||||
expect(renameButton.hasAttribute('disabled')).to.be.false
|
||||
const saveButton = within(modal).getByRole('button', { name: 'Save' })
|
||||
expect(saveButton.hasAttribute('disabled')).to.be.false
|
||||
|
||||
await fireEvent.click(renameButton)
|
||||
await fireEvent.click(saveButton)
|
||||
|
||||
await waitFor(() => expect(fetchMock.called(`/tag/abc123def456/rename`)))
|
||||
|
||||
@@ -243,7 +249,7 @@ describe('<TagsList />', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete modal', function () {
|
||||
describe('Delete modal', function () {
|
||||
beforeEach(async function () {
|
||||
const tag1Button = screen.getByText('Another tag')
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import React from 'react'
|
||||
import { ColorPickerProvider } from '../../../../../frontend/js/features/project-list/context/color-picker-context'
|
||||
import { ProjectListProvider } from '../../../../../frontend/js/features/project-list/context/project-list-context'
|
||||
import { Project } from '../../../../../types/project/dashboard/api'
|
||||
import { projectsData } from '../fixtures/projects-data'
|
||||
@@ -33,7 +34,11 @@ export function renderWithProjectListContext(
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) => <ProjectListProvider>{children}</ProjectListProvider>
|
||||
}) => (
|
||||
<ProjectListProvider>
|
||||
<ColorPickerProvider>{children}</ColorPickerProvider>
|
||||
</ProjectListProvider>
|
||||
)
|
||||
|
||||
return render(component, {
|
||||
wrapper: ProjectListProviderWrapper,
|
||||
|
||||
Reference in New Issue
Block a user