From bc621066b74f0da9684304aab4c7cdcc7e2520ee Mon Sep 17 00:00:00 2001 From: KamilKranczuk Date: Tue, 22 Aug 2023 10:12:15 +0200 Subject: [PATCH 1/4] Exclude deleted content --- .../Api/Features/ContentType/ContentTypeController.cs | 6 ++++-- .../ContentType/ContentTypeUsagesRepository.cs | 10 ++++++++-- .../Api/Features/ContentType/GetContentTypesQuery.cs | 4 ++++ .../Api/Services/ContentTypeService.cs | 6 ++++-- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeController.cs b/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeController.cs index 17eb51b..812f046 100644 --- a/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeController.cs +++ b/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeController.cs @@ -48,14 +48,16 @@ public ActionResult GetContentType([FromQuery] [BindRequired] Guid guid) [HttpGet] [Route("[action]", Name = GetContentTypesRouteName)] [SwaggerResponse(StatusCodes.Status200OK, null, typeof(IEnumerable))] - public async Task GetContentTypes([FromQuery] GetContentTypesQuery? query, CancellationToken cancellationToken) + public async Task GetContentTypes([FromQuery] GetContentTypesQuery query, CancellationToken cancellationToken) { + query!.IncludeDeleted ??= true; + var contentTypesFilterCriteria = new ContentTypesFilterCriteria { Name = query?.Name, Type = query?.Type }; var contentTypes = _contentTypeService.GetAll(contentTypesFilterCriteria); - var contentTypesUsageCounters = await _contentTypeService.GetAllCounters(cancellationToken); + var contentTypesUsageCounters = await _contentTypeService.GetAllCounters(query.IncludeDeleted.Value, cancellationToken); var contentTypeDtos = contentTypes.Select(_contentTypeMapper.Map); contentTypeDtos = contentTypeDtos.Join(contentTypesUsageCounters, type => type.ID, counter => counter.ContentTypeId, (type, counter) => diff --git a/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeUsagesRepository.cs b/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeUsagesRepository.cs index 28a5ae7..913909f 100644 --- a/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeUsagesRepository.cs +++ b/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/ContentTypeUsagesRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Threading; using System.Threading.Tasks; using EPiServer.Data; @@ -16,13 +17,17 @@ public ContentTypeUsagesRepository(ServiceAccessor dataE _dataExecutorAccessor = dataExecutorAccessor; } - public async Task> ListContentTypesUsagesCounters( + public async Task> ListContentTypesUsagesCounters(bool includeDeleted, CancellationToken cancellationToken) { var executor = _dataExecutorAccessor(); return await executor.ExecuteAsync((Func>>)(async () => { - var command1 = executor.CreateCommand(); + var deletedParam = executor.CreateParameter("Deleted", DbType.Byte, ParameterDirection.Input, includeDeleted); + + var command1 = executor.CreateCommand(); + command1.CommandType = CommandType.Text; + command1.Parameters.Add(deletedParam); command1.CommandText = @"DECLARE @Results TABLE ( ContentTypeId INT, Scope NVARCHAR(255) @@ -75,6 +80,7 @@ INNER JOIN tblPageLanguage ON tblWorkPage.fkPageID=tblPageLanguage.fkPageID INNER JOIN tblLanguageBranch ON tblLanguageBranch.pkID=tblWorkPage.fkLanguageBranchID + WHERE (tblPage.Deleted = @Deleted AND @Deleted = 0) OR @Deleted = 1 GROUP BY tblPage.fkPageTypeID, tblPage.pkID, tblLanguageBranch.LanguageID ) as ContentTypeUsageTable RIGHT OUTER JOIN (SELECT CT.pkID AS ID diff --git a/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/GetContentTypesQuery.cs b/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/GetContentTypesQuery.cs index 997b162..d83d8c2 100644 --- a/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/GetContentTypesQuery.cs +++ b/src/Forte.Optimizely.ContentUsage/Api/Features/ContentType/GetContentTypesQuery.cs @@ -1,9 +1,13 @@  +using Reinforced.Typings.Attributes; + namespace Forte.Optimizely.ContentUsage.Api.Features.ContentType; +[TsInterface] public class GetContentTypesQuery { public string? Name { get; set; } public string? Type { get; set; } public ContentTypesSorting? SortBy { get; set; } + public bool? IncludeDeleted { get; set; } } \ No newline at end of file diff --git a/src/Forte.Optimizely.ContentUsage/Api/Services/ContentTypeService.cs b/src/Forte.Optimizely.ContentUsage/Api/Services/ContentTypeService.cs index 2ab222b..fc73585 100644 --- a/src/Forte.Optimizely.ContentUsage/Api/Services/ContentTypeService.cs +++ b/src/Forte.Optimizely.ContentUsage/Api/Services/ContentTypeService.cs @@ -32,9 +32,11 @@ public IEnumerable GetAll(ContentTypesFilterCriteria? filterCriteri return contentTypes; } - public async Task> GetAllCounters(CancellationToken cancellationToken) + public async Task> GetAllCounters( + bool includeDeleted, + CancellationToken cancellationToken) { - return await _contentTypeUsagesRepository.ListContentTypesUsagesCounters(cancellationToken); + return await _contentTypeUsagesRepository.ListContentTypesUsagesCounters(includeDeleted, cancellationToken); } public ContentType? Get(Guid guid) From d24134956f43185100ce0d4cfe309ef17249bda6 Mon Sep 17 00:00:00 2001 From: KamilKranczuk Date: Tue, 22 Aug 2023 10:14:03 +0200 Subject: [PATCH 2/4] Integrate include/exclude deleted content on FE side --- .../Frontend/Components/Filters/Filters.tsx | 13 ++ .../Frontend/Components/Router.tsx | 3 +- .../Frontend/Lib/ContentUsageAPIClient.ts | 7 +- .../Lib/hooks/useFilteredTableData.ts | 30 +++ .../Frontend/Views/ContentTypesView.tsx | 207 ++++++++++-------- .../Frontend/translations.ts | 1 + 6 files changed, 170 insertions(+), 91 deletions(-) diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.tsx b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.tsx index d0ed9c8..c1dd945 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.tsx +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.tsx @@ -5,6 +5,7 @@ import ContentTypeBasesFilter from "./ContentTypeBasesFilter"; import "./Filters.scss"; import NumberOfRowsFilter from "./NumberOfRowsFilter"; import SearchInput from "./SearchInput"; +import IncludeDeletedFilter from "./IncludeDeletedFilter"; interface FiltersProps { searchValue?: string; @@ -17,6 +18,8 @@ interface FiltersProps { rowsPerPageOptions?: number[]; selectedRowsPerPage?: number; onRowsPerPageChange?: (option: number) => void; + includeDeleted?: boolean; + onIncludeDeletedChange?: React.ChangeEventHandler; } function Filters({ @@ -30,6 +33,8 @@ function Filters({ rowsPerPageOptions, selectedRowsPerPage, onRowsPerPageChange, + includeDeleted, + onIncludeDeletedChange, }: FiltersProps) { return (
@@ -57,6 +62,14 @@ function Filters({ onChange={onRowsPerPageChange} /> )} + + {typeof includeDeleted !== "undefined" && !!onIncludeDeletedChange && ( + + )} +
); } diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Router.tsx b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Router.tsx index 8278998..3a79dbd 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Router.tsx +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Router.tsx @@ -27,7 +27,8 @@ interface LoadDataFunction { ): Promise | Response; } -const contentTypesLoader: LoadDataFunction = (api) => { +const contentTypesLoader: LoadDataFunction = (api, initialLoad) => { + if(!initialLoad) return Promise.resolve([{}, {}]); const contentTypeBases = api.getContentTypeBases(); const contentTypes = api.getContentTypes(); diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Lib/ContentUsageAPIClient.ts b/src/Forte.Optimizely.ContentUsage/Frontend/Lib/ContentUsageAPIClient.ts index 2a11653..64611f1 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Lib/ContentUsageAPIClient.ts +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Lib/ContentUsageAPIClient.ts @@ -5,6 +5,7 @@ import { ContentTypeBaseDto, GetContentUsagesQuery, GetContentUsagesResponse, + GetContentTypesQuery, } from "../dtos"; export interface ContentUsageAPIEndpoints { @@ -59,8 +60,8 @@ export default class ContentUsageAPIClient { return this.get(this.endpoints.contentTypeBases); } - public async getContentTypes() { - return this.get(this.endpoints.contentTypes); + public async getContentTypes(query?: Partial) { + return this.getWithQuerySchema, ContentTypeDto[]>(this.endpoints.contentTypes, query); } public async getContentType(guid: string) { @@ -73,7 +74,7 @@ export default class ContentUsageAPIClient { ): Promise> { const params = {} as Record; - for (const [key, value] of Object.entries(query)) { + for (const [key, value] of Object.entries(query ?? {})) { params[key] = value.toString(); } diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Lib/hooks/useFilteredTableData.ts b/src/Forte.Optimizely.ContentUsage/Frontend/Lib/hooks/useFilteredTableData.ts index 7e5ee30..9232929 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Lib/hooks/useFilteredTableData.ts +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Lib/hooks/useFilteredTableData.ts @@ -12,6 +12,7 @@ enum FilteredTableDataQueryParam { ShowColumn = "showColumn", Page = "page", RowsPerPage = "rowsPerPage", + IncludeDeleted = "includeDeleted" } interface FilteredTableDataHookOptions { @@ -59,6 +60,8 @@ export function useFilteredTableData({ totalPages: number; currentPage: number; goToPage: (page: number) => void; + includeDeleted: boolean; + onIncludeDeletedChange: React.ChangeEventHandler; } { const triggerUpdate = useRef(false); const [datasetChanged, setDatasetChanged] = useState(false); @@ -82,6 +85,7 @@ export function useFilteredTableData({ })) ); const [searchParams, setSearchParams] = useSearchParams(); + const [includeDeleted, setIncludeDeleted] = useState(false); const location = useLocation(); const debouncedSetSearchParams = useDebounce< @@ -102,6 +106,7 @@ export function useFilteredTableData({ sortDirection: false, rowsPerPage: false, contentTypeBases: false, + includeDeleted: false, }); useEffect(() => { @@ -172,6 +177,10 @@ export function useFilteredTableData({ ); } + if (changesTracker.includeDeleted){ + prevSearchParams.set(FilteredTableDataQueryParam.IncludeDeleted, String(includeDeleted)); + } + triggerUpdate.current = false; setChangesTracker({ currentPage: false, @@ -181,6 +190,7 @@ export function useFilteredTableData({ sortDirection: false, rowsPerPage: false, contentTypeBases: false, + includeDeleted: false, }); return prevSearchParams; @@ -193,6 +203,7 @@ export function useFilteredTableData({ sortDirection, rowsPerPage, contentTypeBases, + includeDeleted ]); const setPageToStart = useCallback(() => { @@ -265,6 +276,15 @@ export function useFilteredTableData({ [tableColumns, setTableColumns, setSortBy, sortBy] ); + const onIncludeDeletedChange: React.ChangeEventHandler = useCallback((event) => { + const newIncludeDeletedValue = event.target.checked; + setIncludeDeleted(newIncludeDeletedValue); + setChangesTracker({ + ...changesTracker, + includeDeleted: true + }) + }, [changesTracker, setChangesTracker, setIncludeDeleted]) + const onColumnVisiblityChange = useCallback( (id: string, visible: boolean) => { if (tableColumns.filter((column) => column.visible).length > 1 || visible) @@ -543,6 +563,13 @@ export function useFilteredTableData({ } else { setPageToStart(); } + + if (queryParams.has(FilteredTableDataQueryParam.IncludeDeleted)) { + const param = queryParams.get(FilteredTableDataQueryParam.IncludeDeleted).toLocaleLowerCase() === 'true'; + setIncludeDeleted(param); + } else { + setIncludeDeleted(false); + } }, [ tableColumns, @@ -555,6 +582,7 @@ export function useFilteredTableData({ setSearchQuery, setSortBy, setSortDirection, + setIncludeDeleted, initialSortDirection, initialContentTypeBases, initialTableColumns, @@ -615,5 +643,7 @@ export function useFilteredTableData({ totalPages, currentPage, goToPage: handlePageChange, + includeDeleted, + onIncludeDeletedChange }; } diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Views/ContentTypesView.tsx b/src/Forte.Optimizely.ContentUsage/Frontend/Views/ContentTypesView.tsx index c24a652..b522b8b 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Views/ContentTypesView.tsx +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Views/ContentTypesView.tsx @@ -11,6 +11,7 @@ import { GridCell, GridContainer, PaginationControls, + Spinner, Table, } from "optimizely-oui"; import { useLoaderData, useNavigate } from "react-router-dom"; @@ -25,6 +26,7 @@ import { useTranslations } from "../Contexts/TranslationsProvider"; import { useFilteredTableData } from "../Lib/hooks/useFilteredTableData"; import { TableColumn } from "../types"; import { useScroll } from "../Lib/hooks/useScroll"; +import { useAPI } from "../Contexts/ApiProvider"; enum ContentTypesTableColumn { GUID = "guid", @@ -36,13 +38,15 @@ enum ContentTypesTableColumn { } const ContentTypesView = () => { - const [dataLoaded, setDataLoaded] = useState(false); + const [initialDataLoaded, setInitialDataLoaded] = useState(false); + const [dataLoading, setDataLoading] = useState(false); const translations = useTranslations(); const [initialContentTypeBases, setInitialContentTypeBases] = useState< ContentTypeBaseDto[] >([]); const [contentTypes, setContentTypes] = useState([]); const navigate = useNavigate(); + const api = useAPI(); const gridContainerRef = useRef(); const { scrollTo } = useScroll(); @@ -117,6 +121,8 @@ const ContentTypesView = () => { totalPages, currentPage, goToPage, + includeDeleted, + onIncludeDeletedChange } = useFilteredTableData({ rows: contentTypes, initialTableColumns, @@ -136,8 +142,22 @@ const ContentTypesView = () => { APIResponse ]; + const handleIncludeDeletedChange = useCallback((event: React.ChangeEvent) => { + setDataLoading(true); + onIncludeDeletedChange(event); + },[setDataLoading, onIncludeDeletedChange]) + useEffect(() => { - if (!dataLoaded && response && Array.isArray(response)) { + api.getContentTypes({includeDeleted}).then(response => { + if (!response.hasErrors && response.data) { + setContentTypes(response.data); + } + setDataLoading(false); + }); + }, [includeDeleted, setDataLoading]) + + useEffect(() => { + if (!initialDataLoaded && response && Array.isArray(response)) { const [contentTypeBasesResponse, contentTypesResponse] = response; if ( @@ -156,13 +176,16 @@ const ContentTypesView = () => { setContentTypes(contentTypesResponse.data); } - setDataLoaded(true); + setInitialDataLoaded(true); } - }, [response, dataLoaded]); + }, [response, initialDataLoaded]); return ( - +
@@ -179,99 +202,109 @@ const ContentTypesView = () => { onTableColumnChange={onTableColumnChange} selectedRowsPerPage={selectedRowsPerPage} onRowsPerPageChange={onRowsPerPageChange} + includeDeleted={includeDeleted} + onIncludeDeletedChange={handleIncludeDeletedChange} /> -
- 0} - > - - - {tableColumns - .filter((column) => column.visible) - .map((column) => ( - onSortChange(column), - order: sortDirection, - }} - key={column.id} - > - {column.name} - - ))} - - - - - - {rows.length > 0 ? ( - rows.map((row) => ( - - {tableColumns - .filter((column) => column.visible) - .map((column) => ( - - {row[column.id]} - - ))} - - - } + {dataLoading ? ( +
+ +
+ ) : ( +
+
0} + > + + + {tableColumns + .filter((column) => column.visible) + .map((column) => ( + onSortChange(column), + order: sortDirection, + }} + key={column.id} > - - - - - - - - + {column.name} + + ))} + + + + + + {rows.length > 0 ? ( + rows.map((row) => ( + + {tableColumns + .filter((column) => column.visible) + .map((column) => ( + + {row[column.id]} + + ))} + + + } + > + + - - - - + + + + + + + + + + + + + )) + ) : ( + + + {translations.noResults} - )) - ) : ( - - {translations.noResults} - - )} - -
-
+ )} + + + + )}
{totalPages > 1 && ( diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/translations.ts b/src/Forte.Optimizely.ContentUsage/Frontend/translations.ts index c5619e8..3817d84 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/translations.ts +++ b/src/Forte.Optimizely.ContentUsage/Frontend/translations.ts @@ -9,6 +9,7 @@ export const translations = { numberOfRows: "Number of rows", search: "Search", none: "None", + includeDeleted: "Include deleted content", }, views: { contentTypesView: { From 831fad68fae6aeb6c214797d33c290ca12fc5919 Mon Sep 17 00:00:00 2001 From: KamilKranczuk Date: Tue, 22 Aug 2023 11:44:46 +0200 Subject: [PATCH 3/4] Add IncludeDeletedFilter --- .../Filters/IncludeDeletedFilter.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx new file mode 100644 index 0000000..e32b124 --- /dev/null +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx @@ -0,0 +1,23 @@ +import React, { FC } from "react"; +import { Checkbox } from "optimizely-oui"; +import { useTranslations } from "../../Contexts/TranslationsProvider"; + +interface IncludeDeletedFilterProps { + includeDeleted?: boolean; + onIncludeDeletedChange?: React.ChangeEventHandler; +} + +const IncludeDeletedFilter: FC = ({ + includeDeleted, + onIncludeDeletedChange, +}) => { + const translations = useTranslations(); + + return ( +
+ +
+ ); +}; + +export default IncludeDeletedFilter; From 28a3b3fc6af081324184181aa25d12c2bab5c1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20G=C3=B3rza=C5=84ski?= Date: Wed, 13 Sep 2023 14:34:38 +0200 Subject: [PATCH 4/4] Change Checkbox size and position --- .../Frontend/Components/Filters/Filters.scss | 15 +++++++++- .../Filters/IncludeDeletedFilter.tsx | 15 +++++++--- .../Components/Form/Checkbox/Checkbox.scss | 5 ++++ .../Components/Form/Checkbox/Checkbox.tsx | 30 +++++++++++++++++++ .../Frontend/Styles/_variables.scss | 2 ++ .../Frontend/Utils/styles.ts | 4 +++ .../package.json | 1 + src/Forte.Optimizely.ContentUsage/yarn.lock | 2 +- 8 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.scss create mode 100644 src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.tsx create mode 100644 src/Forte.Optimizely.ContentUsage/Frontend/Utils/styles.ts diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.scss b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.scss index af45923..988c6ac 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.scss +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/Filters.scss @@ -1,7 +1,7 @@ @use 'sass:map'; @import "../../Styles/utils"; -.forte-optimizely-content-usage-filters { +.#{$class-name-prefix}-filters { display: flex; flex-direction: column; margin-bottom: spacer(1); @@ -44,5 +44,18 @@ } } } + + & input[type="checkbox"] { + margin-top: 0 !important; + } + } + + & .#{$class-name-prefix}-filter-checkbox-container { + & > label { + & > div { + height: 100%; + align-items: center; + } + } } } diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx index e32b124..8cb94e2 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Filters/IncludeDeletedFilter.tsx @@ -1,6 +1,8 @@ import React, { FC } from "react"; -import { Checkbox } from "optimizely-oui"; import { useTranslations } from "../../Contexts/TranslationsProvider"; +import Checkbox from "../Form/Checkbox/Checkbox"; +import classNames from "classnames"; +import { classNamePrefix } from "../../Utils/styles"; interface IncludeDeletedFilterProps { includeDeleted?: boolean; @@ -11,11 +13,16 @@ const IncludeDeletedFilter: FC = ({ includeDeleted, onIncludeDeletedChange, }) => { - const translations = useTranslations(); + const translations = useTranslations(); return ( -
- +
+ + {translations.filters.includeDeleted} +
); }; diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.scss b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.scss new file mode 100644 index 0000000..c639f37 --- /dev/null +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.scss @@ -0,0 +1,5 @@ +@import "../../../Styles/utils"; + +.#{$class-name-prefix}-checkbox { + width: 14px; +} \ No newline at end of file diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.tsx b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.tsx new file mode 100644 index 0000000..198b5ee --- /dev/null +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Components/Form/Checkbox/Checkbox.tsx @@ -0,0 +1,30 @@ +import React, { ChangeEvent, ReactNode } from 'react'; +import { Checkbox as OuiCheckbox } from 'optimizely-oui'; +import classNames from 'classnames'; +import { classNamePrefix } from '../../../Utils/styles'; + +import './Checkbox.scss'; + +interface CheckboxProps { + checked?: boolean; + className?: string; + defaultChecked?: boolean; + isDisabled?: boolean; + children?: ReactNode; + labelWeight?: string; + description?: string; + indeterminate?: boolean; + onChange?: (event: ChangeEvent) => void; +} + +const Checkbox = ({ className, children, ...props }: CheckboxProps) => { + return ( + + ); +}; + +export default Checkbox; diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Styles/_variables.scss b/src/Forte.Optimizely.ContentUsage/Frontend/Styles/_variables.scss index 2722068..ad91b75 100644 --- a/src/Forte.Optimizely.ContentUsage/Frontend/Styles/_variables.scss +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Styles/_variables.scss @@ -1 +1,3 @@ @import 'variables/breakpoints'; + +$class-name-prefix: 'forte-optimizely-content-usage'; diff --git a/src/Forte.Optimizely.ContentUsage/Frontend/Utils/styles.ts b/src/Forte.Optimizely.ContentUsage/Frontend/Utils/styles.ts new file mode 100644 index 0000000..7b6387e --- /dev/null +++ b/src/Forte.Optimizely.ContentUsage/Frontend/Utils/styles.ts @@ -0,0 +1,4 @@ +const CLASS_NAME_PREFIX = 'forte-optimizely-content-usage'; + +export const classNamePrefix = (className?: string) => + className ? `${CLASS_NAME_PREFIX}-${className}` : CLASS_NAME_PREFIX; diff --git a/src/Forte.Optimizely.ContentUsage/package.json b/src/Forte.Optimizely.ContentUsage/package.json index 8ed5abd..bf9dfb5 100644 --- a/src/Forte.Optimizely.ContentUsage/package.json +++ b/src/Forte.Optimizely.ContentUsage/package.json @@ -11,6 +11,7 @@ "dependencies": { "@episerver/ui-framework": "^0.17.1", "axios": "^0.27.2", + "classnames": "^2.3.2", "optimizely-oui": "^49.0.0", "react": "^16.11.0", "react-copy-to-clipboard": "^5.1.0", diff --git a/src/Forte.Optimizely.ContentUsage/yarn.lock b/src/Forte.Optimizely.ContentUsage/yarn.lock index aeb3417..6a14647 100644 --- a/src/Forte.Optimizely.ContentUsage/yarn.lock +++ b/src/Forte.Optimizely.ContentUsage/yarn.lock @@ -1838,7 +1838,7 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -classnames@*, classnames@^2.2.5: +classnames@*, classnames@^2.2.5, classnames@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==