Skip to content

Commit

Permalink
Merge pull request #393 from sanger/x1032_add_user_full_name_to_RR
Browse files Browse the repository at this point in the history
x1032 Add user full name to Release Recipient
  • Loading branch information
sabrine33 authored Sep 7, 2023
2 parents 288236a + 570a686 commit b61fc93
Show file tree
Hide file tree
Showing 11 changed files with 1,699 additions and 1,416 deletions.
80 changes: 76 additions & 4 deletions cypress/e2e/pages/configuration.cy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { AddReleaseRecipientMutation, AddReleaseRecipientMutationVariables } from '../../../src/types/sdk';
import {
AddReleaseRecipientMutation,
AddReleaseRecipientMutationVariables,
UpdateReleaseRecipientFullNameMutation,
UpdateReleaseRecipientFullNameMutationVariables
} from '../../../src/types/sdk';
import { selectOption } from '../shared/customReactSelect.cy';

describe('Configuration Spec', () => {
Expand Down Expand Up @@ -116,6 +121,39 @@ describe('Configuration Spec', () => {
});
});

describe('displays extra input field when specified', () => {
const config = {
name: 'Release Recipients',
tabName: 'Release Recipients',
field: 'cs41',
extraFieldValue: 'Csaba Csordas',
buttonName: '+ Add Username',
newValue: 'az99',
newExtraFieldValue: 'Arielle Zimran'
};
context('configuration table should contains extra column to display extra field values', () => {
before(() => {
cy.scrollTo(0, 0);
cy.findByText(config.name).click();
});

it('displays extra field values', () => {
cy.get(`div[data-testid="config"]:contains(${config.name})`)
.find(`tr:contains(${config.field}) `)
.find('input')
.first()
.should('have.value', config.extraFieldValue);
});

it('displays extra field input when adding new entity', () => {
clickButton(config.buttonName);
cy.findByTestId('input-field').scrollIntoView().focus().type(`${config.newValue}`);
enterNewExtraFieldValue(config.extraFieldValue);
cy.findByText('Saved').scrollIntoView().should('be.visible');
});
});
});

context('When adding a Release Recipients fails', () => {
before(() => {
cy.msw().then(({ worker, graphql }) => {
Expand All @@ -134,8 +172,6 @@ describe('Configuration Spec', () => {
)
);
});
cy.scrollTo(0, 0);
cy.findByText('Release Recipients').click();
});

it('shows an error message', () => {
Expand All @@ -147,8 +183,40 @@ describe('Configuration Spec', () => {
});
});
});

context('When Updating a Release Recipient full name successfully', () => {
before(() => {
cy.msw().then(({ worker, graphql }) => {
worker.use(
graphql.mutation<UpdateReleaseRecipientFullNameMutation, UpdateReleaseRecipientFullNameMutationVariables>(
'UpdateReleaseRecipientFullName',
(req, res, ctx) => {
return res.once(
ctx.data({
updateReleaseRecipientFullName: {
username: 'et2',
fullName: 'Ethan Twin',
enabled: true
}
})
);
}
)
);
});
cy.get(`div[data-testid="config"]:contains('Release Recipients')`)
.find(`tr:contains('et2') `)
.find('input')
.first()
.focus()
.type(`Ethan Twin {enter}`, { force: true });
});
it('shows a success message', () => {
cy.findByText('Changes for "et2" saved').should('be.visible');
});
});
function selectElement(findTag: string) {
return cy.get(findTag).scrollIntoView().click({
return cy.get(findTag).last().scrollIntoView().click({
force: true
});
}
Expand All @@ -158,4 +226,8 @@ describe('Configuration Spec', () => {
function enterNewValue(value: string) {
cy.findByTestId('input-field').scrollIntoView().focus().type(`${value}{enter}`, { force: true });
}

function enterNewExtraFieldValue(value: string) {
cy.findByTestId('extra-input-field').scrollIntoView().focus().type(`${value}{enter}`, { force: true });
}
});
5 changes: 3 additions & 2 deletions src/components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface TableHeaderProps {
sortProps?: SortProps;
children?: ReactNode | ReactNode[];
allCapital?: boolean;
colSpan?: number;
}
/**
* @example
Expand Down Expand Up @@ -58,9 +59,9 @@ export const TableHead = ({ children, fixed = false }: TableHeadProps) => {
return <thead className={`${fixed ? 'sticky top-0' : ''}`}>{children}</thead>;
};

export const TableHeader = ({ children, sortProps, allCapital = true, ...rest }: TableHeaderProps) => {
export const TableHeader = ({ children, sortProps, allCapital = true, colSpan, ...rest }: TableHeaderProps) => {
return (
<th className="px-6 py-3 bg-gray-50 text-left select-none" {...rest}>
<th className="px-6 py-3 bg-gray-50 text-left select-none" colSpan={colSpan} {...rest}>
<>
{
<IconButton
Expand Down
147 changes: 116 additions & 31 deletions src/components/entityManager/EntityManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Input } from '../forms/Input';
import BlueButton from '../buttons/BlueButton';
import { useMachine } from '@xstate/react';
import Success from '../notifications/Success';
import Warning from '../notifications/Warning';
import { capitalize } from 'lodash';
import WhiteButton from '../buttons/WhiteButton';
import PinkButton from '../buttons/PinkButton';
Expand All @@ -14,6 +13,7 @@ import { BooleanEntityRow } from './BooleanEntityRow';
import { createEntityManagerMachine } from './entityManager.machine';
import { alphaNumericSortDefault } from '../../types/stan';
import CustomReactSelect, { OptionType } from '../forms/CustomReactSelect';
import Warning from '../notifications/Warning';

export type EntityValueType = boolean | string | number;

Expand All @@ -23,6 +23,14 @@ type ValueFieldComponentInfo = {
valueOptions?: string[];
};

type ExtraEntityColumn<E> = {
label: string;
value: string;
keyFieldPlaceholder: string;
extraFieldPlaceholder: string;
onChange(value: string, extraValue?: string): Promise<E>;
};

type EntityManagerProps<E> = {
/**
* The initial entities to display in the table
Expand Down Expand Up @@ -55,7 +63,7 @@ type EntityManagerProps<E> = {
* Callback when a new entity is to be created
* @param value the value of the new entity
*/
onCreate(value: string): Promise<E>;
onCreate(value: string, extraValue?: string): Promise<E>;

/**
* Callback when value changes
Expand All @@ -66,8 +74,12 @@ type EntityManagerProps<E> = {
* Display key field as a dropdown
*/
displayKeyFieldAsDropDown?: boolean;
};

/**
* Extra property of the entity to display in the table
*/
extraDisplayColumnName?: ExtraEntityColumn<E>;
};
export default function EntityManager<E extends Record<string, EntityValueType>>({
initialEntities,
displayKeyColumnName,
Expand All @@ -76,22 +88,27 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
valueFieldComponentInfo,
onCreate,
onChangeValue,
displayKeyFieldAsDropDown = false
displayKeyFieldAsDropDown = false,
extraDisplayColumnName = undefined
}: EntityManagerProps<E>) {
const entityManagerMachine = React.useMemo(() => {
return createEntityManagerMachine<E>(initialEntities, displayKeyColumnName, valueColumnName).withConfig({
services: {
createEntity: (ctx, e) => {
if (e.type !== 'CREATE_NEW_ENTITY') return Promise.reject();
return onCreate(e.value);
return onCreate(e.value, e.extraValue);
},
valueChanged: (context, e) => {
if (e.type !== 'VALUE_CHANGE') return Promise.reject();
return onChangeValue(e.entity, e.value);
},
updateExtraProperty: (context, e) => {
if (e.type !== 'EXTRA_PROPERTY_UPDATE_VALUE' || !extraDisplayColumnName) return Promise.reject();
return extraDisplayColumnName.onChange(e.value, e.extraValue);
}
}
});
}, [initialEntities, displayKeyColumnName, valueColumnName, onChangeValue, onCreate]);
}, [initialEntities, displayKeyColumnName, valueColumnName, onChangeValue, onCreate, extraDisplayColumnName]);

const [current, send] = useMachine(entityManagerMachine);

Expand All @@ -118,6 +135,7 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
* Will receive focus when it appears on screen.
*/
const inputRef = useRef<HTMLInputElement>(null);
const extraInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (current.matches('draftCreation')) {
inputRef.current?.select();
Expand All @@ -132,9 +150,13 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
if (value === '') {
return;
}
send({ type: 'CREATE_NEW_ENTITY', value });
let extraValue: string | undefined;
if (extraDisplayColumnName) {
extraValue = extraInputRef.current?.value.trim();
}
send({ type: 'CREATE_NEW_ENTITY', value, extraValue });
setDraftValue('');
}, [draftValue, setDraftValue, send]);
}, [draftValue, setDraftValue, send, extraDisplayColumnName]);

/**
* Callback handler for when an EntityRow changes (i.e. enabled property is toggled)
Expand Down Expand Up @@ -171,6 +193,28 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
*/
const handleOnCancel = () => send({ type: 'DISCARD_DRAFT' });

const handleExtraValueUpdate = useCallback(
(keyValue: string, extraValue: string) => {
send({
type: 'EXTRA_PROPERTY_UPDATE_VALUE',
value: keyValue,
extraValue: extraValue
});
},
[send]
);

const handleOnChangeForExtraDisplayColumn = useCallback(
(entity: E, extraValue: string) => {
if (!extraDisplayColumnName) return;
send({
type: 'EXTRA_PROPERTY_DRAFT_VALUE',
entity: { ...entity, [extraDisplayColumnName.value]: extraValue }
});
},
[send, extraDisplayColumnName]
);

const getValueFieldComponent = (
valueFieldComponentInfo: ValueFieldComponentInfo,
entity: E | undefined,
Expand Down Expand Up @@ -204,6 +248,47 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
return <></>;
}
};
const setInputFieldsRow = () => {
return (
<tr>
<TableCell colSpan={2}>
<Input
type="text"
placeholder={extraDisplayColumnName ? extraDisplayColumnName.keyFieldPlaceholder : ''}
ref={inputRef}
data-testid="input-field"
disabled={isCreatingEntity}
onChange={handleOnInputChange}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleOnSave();
} else if (e.key === 'Escape') {
handleOnCancel();
}
}}
/>
</TableCell>
{extraDisplayColumnName && (
<TableCell colSpan={2}>
<Input
type="text"
placeholder={extraDisplayColumnName.extraFieldPlaceholder}
ref={extraInputRef}
data-testid="extra-input-field"
disabled={isCreatingEntity}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleOnSave();
} else if (e.key === 'Escape') {
handleOnCancel();
}
}}
/>
</TableCell>
)}
</tr>
);
};

return (
<div className="space-y-4">
Expand All @@ -212,8 +297,9 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
<Table>
<TableHead>
<tr>
<TableHeader>{displayKeyColumnName}</TableHeader>
<TableHeader>{valueColumnName}</TableHeader>
<TableHeader colSpan={2}>{displayKeyColumnName}</TableHeader>
{extraDisplayColumnName && <TableHeader colSpan={2}>{extraDisplayColumnName.label}</TableHeader>}
<TableHeader colSpan={2}>{valueColumnName}</TableHeader>
</tr>
</TableHead>
<TableBody>
Expand Down Expand Up @@ -241,7 +327,25 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
) : (
orderedEntities.map((entity, indx) => (
<tr key={indx}>
<TableCell>{entity[displayKeyColumnName]}</TableCell>
<TableCell colSpan={2}>{entity[displayKeyColumnName]}</TableCell>
{extraDisplayColumnName && (
<TableCell colSpan={2}>
<Input
type="text"
placeholder={extraDisplayColumnName.extraFieldPlaceholder}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleExtraValueUpdate(String(entity[displayKeyColumnName]), e.currentTarget.value);
e.currentTarget.blur();
}
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleOnChangeForExtraDisplayColumn(entity, e.target.value);
}}
value={String(entity[extraDisplayColumnName.value])}
/>
</TableCell>
)}
{getValueFieldComponent(
valueFieldComponentInfo,
entity,
Expand All @@ -251,26 +355,7 @@ export default function EntityManager<E extends Record<string, EntityValueType>>
</tr>
))
)}
{showDraft && (
<tr>
<TableCell colSpan={2}>
<Input
ref={inputRef}
data-testid={'input-field'}
type="text"
disabled={isCreatingEntity}
onChange={handleOnInputChange}
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleOnSave();
} else if (e.key === 'Escape') {
handleOnCancel();
}
}}
/>
</TableCell>
</tr>
)}
{showDraft && setInputFieldsRow()}
</TableBody>
</Table>
<div className="flex flex-row justify-end items-center space-x-3">
Expand Down
Loading

0 comments on commit b61fc93

Please sign in to comment.