Skip to content

Commit

Permalink
Merge pull request #930 from Sitecore/changelog/action-required-filter
Browse files Browse the repository at this point in the history
Add additional filter to changelog list
  • Loading branch information
markvanaalst authored Dec 3, 2024
2 parents 22b8a66 + 0f77c01 commit 3e5199f
Show file tree
Hide file tree
Showing 20 changed files with 230 additions and 41 deletions.
6 changes: 3 additions & 3 deletions data/gql/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ const config: CodegenConfig = {
overwrite: true,
schema: [
{
'./src/gql/schema/schema.graphql': {
'./data/gql/schema/schema.graphql': {
assumeValidSDL: true,
},
},
],
documents: ['./src/gql/query/**/*.graphql'],
documents: ['./data/gql/query/**/*.graphql'],
ignoreNoDocuments: false,
generates: {
'./src/gql/generated/': {
'./data/gql/generated/': {
preset: 'client',
config: {
avoidOptionals: {
Expand Down
116 changes: 116 additions & 0 deletions data/gql/custom/getCustomEntryByTitleProductAndChangeTypeQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { SearchByProductsAndChangeTypesQuery, SearchByProductsAndChangeTypesQueryVariables, TypedDocumentString } from '../generated/graphql';

export function getCustomEntryByTitleProductAndChangeTypeQuery(entryTitle: string): TypedDocumentString<SearchByProductsAndChangeTypesQuery, SearchByProductsAndChangeTypesQueryVariables> {
let whereClauseSearchTerm = '';

const searchArray = entryTitle.split('-');

if (searchArray.length > 0) {
whereClauseSearchTerm += `AND: [`;
searchArray.forEach((term: string) => {
whereClauseSearchTerm += `{title_contains: "${term}"}`;
});
whereClauseSearchTerm += `]`;
}

const query = new TypedDocumentString(`
query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "") {
changelog: allChangelog(
orderBy: RELEASEDATE_DESC,
first: $first,
after: $after,
where: {
releaseDate_lt: $date,
OR: [
{ sitecoreProduct: {
changelog_ids: $productIds
}
}
],
AND:
{
OR: [{ changeType: { changelog_ids: $changeTypeIds } }]
${whereClauseSearchTerm}
},
}
) {
pageInfo {
hasNext
endCursor
}
total
results {
...changelogEntry
}
}
}
fragment changeType on Changetype {
id
name
changeType
}
fragment changelogEntry on Changelog {
id
name
title
description
fullArticle
readMoreLink
breakingChange
version
releaseDate
scheduled
image {
total
results {
...media
}
}
sitecoreProduct {
total
results {
...product
}
}
changeType {
total
results {
...changeType
}
}
status {
total
results {
...status
}
}
}
fragment media on Media {
id
name
fileName
fileUrl
description
fileWidth
fileHeight
fileId
fileSize
fileType
}
fragment product on SitecoreProduct {
id
name
productName
productDescription
darkIcon: productIconDark
lightIcon: productIconLight
}
fragment status on Status {
id
name
description
identifier
}`) as unknown as TypedDocumentString<SearchByProductsAndChangeTypesQuery, SearchByProductsAndChangeTypesQueryVariables>;

return query;
}
4 changes: 2 additions & 2 deletions data/gql/generated/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const documents = {
types.SearchByDateDocument,
'query searchByProduct($date: DateTime, $productId: [ID], $changeTypeIds: [ID] = [], $first: Int = 5, $after: String = "") {\n changelog: allChangelog(\n orderBy: RELEASEDATE_DESC\n first: $first\n after: $after\n where: {releaseDate_lt: $date, sitecoreProduct: {changelog_ids: $productId}, AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}\n ) {\n pageInfo {\n hasNext\n endCursor\n }\n total\n results {\n ...changelogEntry\n }\n }\n}':
types.SearchByProductDocument,
'query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "") {\n changelog: allChangelog(\n orderBy: RELEASEDATE_DESC\n first: $first\n after: $after\n where: {releaseDate_lt: $date, OR: [{sitecoreProduct: {changelog_ids: $productIds}}], AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}\n ) {\n pageInfo {\n hasNext\n endCursor\n }\n total\n results {\n ...changelogEntry\n }\n }\n}':
'query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "", $breaking: Boolean = false) {\n changelog: allChangelog(\n orderBy: RELEASEDATE_DESC\n first: $first\n after: $after\n where: {releaseDate_lt: $date, breakingChange_eq: $breaking, OR: [{sitecoreProduct: {changelog_ids: $productIds}}], AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}\n ) {\n pageInfo {\n hasNext\n endCursor\n }\n total\n results {\n ...changelogEntry\n }\n }\n}':
types.SearchByProductsAndChangeTypesDocument,
'query searchByTitle($date: DateTime, $productId: [ID]) {\n data: allChangelog(\n first: 1\n where: {releaseDate_lt: $date, sitecoreProduct: {changelog_ids: $productId}}\n ) {\n pageInfo {\n hasNext\n endCursor\n }\n total\n results {\n ...changelogEntry\n }\n }\n}':
types.SearchByTitleDocument,
Expand Down Expand Up @@ -111,7 +111,7 @@ export function graphql(
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: 'query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "") {\n changelog: allChangelog(\n orderBy: RELEASEDATE_DESC\n first: $first\n after: $after\n where: {releaseDate_lt: $date, OR: [{sitecoreProduct: {changelog_ids: $productIds}}], AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}\n ) {\n pageInfo {\n hasNext\n endCursor\n }\n total\n results {\n ...changelogEntry\n }\n }\n}'
source: 'query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "", $breaking: Boolean = false) {\n changelog: allChangelog(\n orderBy: RELEASEDATE_DESC\n first: $first\n after: $after\n where: {releaseDate_lt: $date, breakingChange_eq: $breaking, OR: [{sitecoreProduct: {changelog_ids: $productIds}}], AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}\n ) {\n pageInfo {\n hasNext\n endCursor\n }\n total\n results {\n ...changelogEntry\n }\n }\n}'
): typeof import('./graphql').SearchByProductsAndChangeTypesDocument;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
Expand Down
5 changes: 3 additions & 2 deletions data/gql/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,7 @@ export type SearchByProductsAndChangeTypesQueryVariables = Exact<{
changeTypeIds: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>> | InputMaybe<Scalars['ID']['input']>>;
first?: InputMaybe<Scalars['Int']['input']>;
after?: InputMaybe<Scalars['String']['input']>;
breaking?: InputMaybe<Scalars['Boolean']['input']>;
}>;

export type SearchByProductsAndChangeTypesQuery = {
Expand Down Expand Up @@ -2033,12 +2034,12 @@ fragment status on Status {
identifier
}`) as unknown as TypedDocumentString<SearchByProductQuery, SearchByProductQueryVariables>;
export const SearchByProductsAndChangeTypesDocument = new TypedDocumentString(`
query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "") {
query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "", $breaking: Boolean = false) {
changelog: allChangelog(
orderBy: RELEASEDATE_DESC
first: $first
after: $after
where: {releaseDate_lt: $date, OR: [{sitecoreProduct: {changelog_ids: $productIds}}], AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}
where: {releaseDate_lt: $date, breakingChange_eq: $breaking, OR: [{sitecoreProduct: {changelog_ids: $productIds}}], AND: {OR: [{changeType: {changelog_ids: $changeTypeIds}}]}}
) {
pageInfo {
hasNext
Expand Down
9 changes: 7 additions & 2 deletions data/gql/query/searchByProductsAndChangeTypes.graphql
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "") {
changelog: allChangelog(orderBy: RELEASEDATE_DESC, first: $first, after: $after, where: { releaseDate_lt: $date, OR: [{ sitecoreProduct: { changelog_ids: $productIds } }], AND: { OR: [{ changeType: { changelog_ids: $changeTypeIds } }] } }) {
query searchByProductsAndChangeTypes($date: DateTime!, $productIds: [ID], $changeTypeIds: [ID], $first: Int = 5, $after: String = "", $breaking: Boolean = false) {
changelog: allChangelog(
orderBy: RELEASEDATE_DESC
first: $first
after: $after
where: { releaseDate_lt: $date, breakingChange_eq: $breaking, OR: [{ sitecoreProduct: { changelog_ids: $productIds } }], AND: { OR: [{ changeType: { changelog_ids: $changeTypeIds } }] } }
) {
pageInfo {
hasNext
endCursor
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"eslint-fix": "npx eslint . --fix",
"configure-husky": "npx husky install && npx husky add .husky/pre-commit \"npx --no-install lint-staged\"",
"analyze": "cross-env ANALYZE=true next build",
"generate": "graphql-codegen --config ./src/gql/codegen.ts",
"generate": "graphql-codegen --config ./data/gql/codegen.ts",
"knip": "knip",
"prettier": "prettier --write .",
"test": "npm run build && npm run test:unit && npm run test:e2e",
Expand Down
2 changes: 1 addition & 1 deletion src/components/changelog/ChangelogEntries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const ChangelogEntries = ({ entries, title = 'Sitecore Changelog', linkHref = '/
)}
{entry.breakingChange && (
<Tag size="sm" colorScheme="danger">
Breaking change
Action required
</Tag>
)}
</HStack>
Expand Down
10 changes: 6 additions & 4 deletions src/components/changelog/ChangelogItemMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ export const ChangelogItemMeta = ({ item }: ChangelogItemMetaProps) => {
</Tag>
))}
{item.breakingChange && (
<Tag size="sm" colorScheme="warning">
Breaking change
</Tag>
<Tooltip label="This change could require manual updates" aria-label="Action required">
<Tag size="sm" colorScheme="warning">
Action required
</Tag>
</Tooltip>
)}
{item.scheduled && (
<Tooltip label="This functionality is scheduled" aria-label="This functionality is scheduled">
<Tooltip label="This functionality is scheduled" aria-label="This functionality is scheduled and not yet released">
<Tag size="sm">Scheduled</Tag>
</Tooltip>
)}
Expand Down
22 changes: 16 additions & 6 deletions src/components/changelog/ChangelogList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Option } from '@/src/components/ui/dropdown';
import { Box, Button, CloseButton, Link, SkeletonText, VisuallyHidden } from '@chakra-ui/react';
import { Alert, AlertIcon, Box, Button, Checkbox, CloseButton, Link, SkeletonText, VisuallyHidden } from '@chakra-ui/react';
import { ChangelogEntry, ChangelogEntryList, Product } from '@lib/changelog/types';
import axios from 'axios';
import NextLink from 'next/link';
Expand All @@ -9,7 +9,6 @@ import useSWRInfinite from 'swr/infinite';

import { entriesApiUrl, getChangeTypeOptions, getProductOptions } from '@/src/lib/changelog/common/changelog';
import { buildQuerystring } from '@/src/lib/changelog/common/querystring';

import ChangelogFilter from './ChangelogFilter';
import ChangelogResultsList from './ChangelogResultsList';
import { Hint } from './Hint';
Expand All @@ -22,6 +21,8 @@ type ChangelogListProps = {

const ChangelogList = ({ initialProduct, selectedProducts, onProductsChange = () => {} }: ChangelogListProps): JSX.Element => {
const [selectedChange, setSelectedChange] = useState<Array<Option>>([]);
const [breaking, setBreaking] = useState<boolean>(false);

const fetcher: Fetcher<ChangelogEntryList<Array<ChangelogEntry>>, string> = async (url: string) => await axios.get(url).then((response) => response.data);

const getKey = (pageIndex: any, previousPageData: ChangelogEntryList<Array<ChangelogEntry>>) => {
Expand All @@ -30,7 +31,7 @@ const ChangelogList = ({ initialProduct, selectedProducts, onProductsChange = ()
}

const cursor = previousPageData ? previousPageData.endCursor : undefined;
const query = buildQuerystring(selectedProducts != null ? selectedProducts : [], selectedChange, cursor, initialProduct);
const query = buildQuerystring(selectedProducts != null ? selectedProducts : [], selectedChange, cursor, initialProduct, breaking);

return [`${entriesApiUrl}?${query.join('&')}`];
};
Expand Down Expand Up @@ -71,18 +72,27 @@ const ChangelogList = ({ initialProduct, selectedProducts, onProductsChange = ()
}}
/>

<Checkbox checked={breaking} onChange={(e) => setBreaking(e.target.checked)}>
Only show changes that might require action
</Checkbox>

<Hint products={selectedProducts} enabled={selectedProducts?.length == 1} />

{isLoading && (
<Box marginTop={8}>
<Box marginTop={12}>
<Placeholder />
<Placeholder />
</Box>
)}

{!error && data && <ChangelogResultsList entries={items} isLoading={isLoading} hasNext={data[data.length - 1].hasNext} onEndTriggered={() => setSize(size + 1)} />}
{!error && data && <ChangelogResultsList entries={items} isLoading={isLoading} hasNext={data[data.length - 1].hasNext} onEndTriggered={() => setSize(size + 1)} mt={8} />}

{data && !data[data.length - 1].hasNext && <span className={`border-violet text-violet dark:border-teal dark:text-teal mt-5 inline-block w-full border-2 px-3 py-2 text-center text-sm`}>No more results</span>}
{data && !data[data.length - 1].hasNext && (
<Alert colorScheme="neutral">
<AlertIcon />
{items.length == 0 ? 'No entries found' : 'No other entries found'}
</Alert>
)}
</Box>
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/components/changelog/ChangelogResultsList.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Box } from '@chakra-ui/react';
import { Box, BoxProps } from '@chakra-ui/react';
import { ChangelogEntry } from '@lib/changelog/types';

import ChangeLogItem from './ChangeLogItem';

type ChangelogResultsListProps = {
type ChangelogResultsListProps = BoxProps & {
entries?: Array<ChangelogEntry>;
isLoading: boolean;
hasNext?: boolean;
onEndTriggered: () => void;
};

const ChangelogResultsList = ({ entries, isLoading, hasNext, onEndTriggered }: ChangelogResultsListProps): JSX.Element => {
return <Box>{entries && entries.map((item, i) => <ChangeLogItem item={item} key={i} loading={isLoading} isLast={i === entries.length - 1} isMore={hasNext} loadEntries={() => onEndTriggered()} />)}</Box>;
const ChangelogResultsList = ({ entries, isLoading, hasNext, onEndTriggered, ...rest }: ChangelogResultsListProps): JSX.Element => {
return <Box {...rest}>{entries && entries.map((item, i) => <ChangeLogItem item={item} key={i} loading={isLoading} isLast={i === entries.length - 1} isMore={hasNext} loadEntries={() => onEndTriggered()} />)}</Box>;
};

export default ChangelogResultsList;
2 changes: 1 addition & 1 deletion src/components/changelog/Hint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const Hint = ({ products, enabled }: HintProps): JSX.Element => {
}

return (
<Alert status="info" colorScheme="neutral" alignItems="center" mb={4}>
<Alert status="info" colorScheme="neutral" alignItems="center" my={4}>
<AlertIcon />
<AlertTitle>
<Tooltip label={`Visit the ${products[0].label} changelog page`} aria-label="A tooltip">
Expand Down
48 changes: 45 additions & 3 deletions src/lib/changelog/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
SearchByTitleQueryVariables,
} from '@data/gql/generated/graphql';

import { getCustomEntryByTitleProductAndChangeTypeQuery } from '@/data/gql/custom/getCustomEntryByTitleProductAndChangeTypeQuery';
import { fetchGraphQL } from './common/fetch';
import { ParseStatus, Status } from './types';
import { ChangelogCredentials } from './types/changelog';
Expand Down Expand Up @@ -106,17 +107,58 @@ export class Changelog {
return ParseRawData(response.data);
}

async getEntriesPaginated(pageSize: string, productId: string, changeTypeId: string, endCursor?: string): Promise<ChangelogEntryList<Array<ChangelogEntry>>> {
return this.getEntries({ productId, changeTypeId, pageSize: Number(pageSize), endCursor });
async getEntriesPaginated(pageSize: string, productId: string, changeTypeId: string, endCursor?: string, breaking?: boolean): Promise<ChangelogEntryList<Array<ChangelogEntry>>> {
return this.getEntries({ productId, changeTypeId, pageSize: Number(pageSize), endCursor, breaking });
}

async getEntries({ productId, changeTypeId, pageSize, endCursor }: { productId?: string; changeTypeId?: string; pageSize?: number; endCursor?: string } = {}): Promise<ChangelogEntryList<Array<ChangelogEntry>>> {
async getEntriesByTitleProductChangeType({
entryTitle,
productId,
changeTypeId,
pageSize,
endCursor,
breaking = false,
}: {
entryTitle?: string;
productId?: string;
changeTypeId?: string;
pageSize?: number;
endCursor?: string;
breaking?: boolean;
} = {}): Promise<ChangelogEntryList<Array<ChangelogEntry>>> {
// If there is no search query provided, return the normal entries
if (entryTitle == undefined) {
return this.getEntries({ productId, changeTypeId, pageSize, endCursor, breaking });
}

const CustomEntryByTitleProductAndChangeTypeDocument = getCustomEntryByTitleProductAndChangeTypeQuery(entryTitle);

const response = await fetchGraphQL<SearchByProductsAndChangeTypesQuery, SearchByProductsAndChangeTypesQueryVariables>(CustomEntryByTitleProductAndChangeTypeDocument, this.credentials, this.isPreview, {
first: pageSize ? pageSize : 5,
after: endCursor ?? '',
date: new Date(),
productIds: productId?.split('|') ?? [],
changeTypeIds: changeTypeId?.split('|') ?? [],
breaking: false,
});

if (response == null) {
return ParseRawData(response);
}

return ParseRawData(response.data);
}

async getEntries({ productId, changeTypeId, pageSize, endCursor, breaking = false }: { productId?: string; changeTypeId?: string; pageSize?: number; endCursor?: string; breaking?: boolean } = {}): Promise<
ChangelogEntryList<Array<ChangelogEntry>>
> {
const response = await fetchGraphQL<SearchByProductsAndChangeTypesQuery, SearchByProductsAndChangeTypesQueryVariables>(SearchByProductsAndChangeTypesDocument, this.credentials, this.isPreview, {
first: pageSize ? pageSize : 5,
after: endCursor ?? '',
date: new Date(),
productIds: productId?.split('|') ?? [],
changeTypeIds: changeTypeId?.split('|') ?? [],
breaking: breaking,
});

if (response == null) {
Expand Down
Loading

0 comments on commit 3e5199f

Please sign in to comment.