Skip to content

Commit

Permalink
Add not open source filter (cncf#583)
Browse files Browse the repository at this point in the history
Signed-off-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
  • Loading branch information
cynthia-sg authored and ES-zxy committed Apr 28, 2024
1 parent fb611e2 commit d0c9d79
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 173 deletions.
19 changes: 17 additions & 2 deletions web/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,22 @@ export const FILTERS: FilterSection[] = [
},
{
value: `non-${getFoundationNameLabel()}`,
name: `Non ${FOUNDATION} Projects`,
name: `Not ${FOUNDATION} Projects`,
},
],
},
{
value: FilterCategory.License,
title: 'License',
options: [
{
value: 'non-oss',
name: 'Not Open Source',
},
{
value: 'oss',
name: 'Open Source',
suboptions: [],
},
],
},
Expand All @@ -140,7 +155,7 @@ export const FILTERS: FilterSection[] = [
},
{
value: 'non_profit',
name: 'Non profit',
name: 'Not profit',
},
],
},
Expand Down
252 changes: 154 additions & 98 deletions web/src/layout/common/ActiveFiltersList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import isEmpty from 'lodash/isEmpty';
import isUndefined from 'lodash/isUndefined';
import startCase from 'lodash/startCase';
import { For, Match, Show, Switch } from 'solid-js';
import { batch, createEffect, createSignal, For, Match, on, Show, Switch } from 'solid-js';

import { REGEX_UNDERSCORE } from '../../data';
import { ActiveFilters, FilterCategory, SVGIconKind } from '../../types';
Expand All @@ -14,17 +14,30 @@ import SVGIcon from './SVGIcon';
interface Props {
activeFilters: ActiveFilters;
maturityOptions?: string[];
licenseOptions?: string[];
resetFilters: () => void;
resetFilter?: (name: FilterCategory) => void;
removeFilter: (name: FilterCategory, value: string) => void;
}

const ActiveFiltersList = (props: Props) => {
const activeFilters = () => props.activeFilters;
const initialActiveFilters = () => props.activeFilters;
const [activeFilters, setActiveFilters] = createSignal<ActiveFilters>({});
const [activeFiltersKeys, setActiveFiltersKeys] = createSignal<FilterCategory[]>([]);
const licenseOptions = () => props.licenseOptions;

const onResetFilters = () => {
props.resetFilters();
};
createEffect(
on(initialActiveFilters, () => {
const keys: FilterCategory[] = [];
Object.keys(initialActiveFilters()).forEach((key) => {
if (!isEmpty(initialActiveFilters()[key as FilterCategory])) keys.push(key as FilterCategory);
});

batch(() => {
setActiveFilters(initialActiveFilters());
setActiveFiltersKeys(keys);
});
})
);

return (
<Show when={Object.keys(activeFilters()).length > 0}>
Expand All @@ -33,13 +46,16 @@ const ActiveFiltersList = (props: Props) => {
class={`d-flex flex-row align-items-center text-nowrap text-muted text-uppercase me-3 mt-2 ${styles.btnLegend}`}
>
<small>Filters applied</small>
<button class={`btn btn-link btn-sm text-muted p-0 ps-1 ${styles.btnReset}`} onClick={onResetFilters}>
<button
class={`btn btn-link btn-sm text-muted p-0 ps-1 ${styles.btnReset}`}
onClick={() => props.resetFilters()}
>
(reset all)
</button>
<small>:</small>
</div>
<div class="d-flex flex-row flex-wrap">
<For each={Object.keys(activeFilters())}>
<For each={activeFiltersKeys()}>
{(f: string) => {
const activeFiltersPerCategory = () => activeFilters()[f as FilterCategory];
if (isUndefined(activeFiltersPerCategory()) || isEmpty(activeFiltersPerCategory())) return null;
Expand All @@ -48,107 +64,147 @@ const ActiveFiltersList = (props: Props) => {
!isUndefined(props.maturityOptions) &&
f === FilterCategory.Maturity &&
props.maturityOptions.every((element) => activeFiltersPerCategory()!.includes(element));

const allLicensesSelected = () =>
!isUndefined(licenseOptions()) &&
licenseOptions()!.length > 0 &&
f === FilterCategory.License &&
licenseOptions()!.every((element) => activeFiltersPerCategory()!.includes(element));

const foundationLabel = getFoundationNameLabel();

return (
<Switch>
<Match when={allMaturitySelected()}>
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span class="text-uppercase">{foundationLabel}</span>
<>
<Switch>
<Match when={allMaturitySelected()}>
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span class="text-uppercase">{foundationLabel}</span>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() => props.removeFilter(f as FilterCategory, foundationLabel)}
aria-label={`Remove ${foundationLabel} filter`}
title={`Remove ${foundationLabel} filter`}
>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() =>
!isUndefined(props.resetFilter) ? props.resetFilter(f as FilterCategory) : null
}
aria-label={`Remove ${foundationLabel} filter`}
title={`Remove ${foundationLabel} filter`}
>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
</span>
</Match>
</span>
</Match>

<Match when={f === FilterCategory.Extra}>
<For each={activeFiltersPerCategory()}>
{(c: string) => {
return (
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
<Match when={allLicensesSelected()}>
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span class="text-uppercase">Open Source</span>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() => props.removeFilter(f as FilterCategory, 'oss')}
aria-label={`Remove open source filter`}
title={`Remove open source filter`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<span class="text-uppercase">{c}</span>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() => props.removeFilter(f as FilterCategory, c)}
aria-label={`Remove ${c} filter`}
title={`Remove ${c} filter`}
>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
</span>
);
}}
</For>
</Match>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
</span>
</Match>

<Match when={!allMaturitySelected()}>
<For each={activeFiltersPerCategory()}>
{(c: string) => {
return (
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span
class={
[FilterCategory.Maturity, FilterCategory.OrgType].includes(f as FilterCategory)
? 'text-uppercase'
: ''
}
<Match when={f === FilterCategory.Extra}>
<For each={activeFiltersPerCategory()}>
{(c: string) => {
return (
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<span class="text-uppercase">{c}</span>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() => props.removeFilter(f as FilterCategory, c)}
aria-label={`Remove ${c} filter`}
title={`Remove ${c} filter`}
>
<Switch fallback={<>{c}</>}>
<Match when={f === FilterCategory.OrgType}>
<>{formatProfitLabel(c)}</>
</Match>
<Match when={f === FilterCategory.TAG}>
<span class="text-uppercase">{formatTAGName(c)}</span>
</Match>
<Match when={f === FilterCategory.InvestmentType}>
<>{startCase(c.replace(REGEX_UNDERSCORE, ' '))}</>
</Match>
</Switch>
</span>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() => props.removeFilter(f as FilterCategory, c)}
aria-label={`Remove ${c} filter`}
title={`Remove ${c} filter`}
</span>
);
}}
</For>
</Match>
</Switch>
<For each={activeFiltersPerCategory()}>
{(c: string) => {
if (
(allMaturitySelected() && c !== `non-${getFoundationNameLabel()}`) ||
(allLicensesSelected() && c !== 'non-oss')
)
return null;

return (
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span
class={
[FilterCategory.Maturity, FilterCategory.OrgType].includes(f as FilterCategory)
? 'text-uppercase'
: ''
}
>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
<Switch fallback={<>{c}</>}>
<Match when={f === FilterCategory.OrgType}>
<>{formatProfitLabel(c)}</>
</Match>
<Match when={f === FilterCategory.TAG}>
<span class="text-uppercase">{formatTAGName(c)}</span>
</Match>
<Match when={f === FilterCategory.InvestmentType}>
<>{startCase(c.replace(REGEX_UNDERSCORE, ' '))}</>
</Match>
<Match when={f === FilterCategory.License && c === 'non-oss'}>
<span class="text-uppercase">Not open source</span>
</Match>
<Match
when={f === FilterCategory.Maturity && c === `non-${getFoundationNameLabel()}`}
>
<span class="text-uppercase">Not {getFoundationNameLabel()} project</span>
</Match>
</Switch>
</span>
</div>
</span>
);
}}
</For>
</Match>
</Switch>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() => props.removeFilter(f as FilterCategory, c)}
aria-label={`Remove ${c} filter`}
title={`Remove ${c} filter`}
>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
</span>
);
}}
</For>
</>
);
}}
</For>
Expand Down
15 changes: 5 additions & 10 deletions web/src/layout/common/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import isUndefined from 'lodash/isUndefined';
import { For, Show } from 'solid-js';

import { FilterCategory, FilterOption, FilterSection } from '../../types';
import getFoundationNameLabel from '../../utils/getFoundationNameLabel';
import CheckBox from './Checkbox';
import styles from './Section.module.css';

interface Props {
section?: FilterSection;
extraMaturity?: FilterSection;
extraOptions?: { [key: string]: FilterSection | undefined };
activeFilters?: string[];
colClass?: string;
title?: string;
Expand Down Expand Up @@ -45,7 +44,7 @@ const Section = (props: Props) => {
if (isUndefined(props.section)) {
return false;
}
if (props.section.value === FilterCategory.Maturity && isUndefined(props.extraMaturity)) {
if (props.section.value === FilterCategory.Maturity && isUndefined(props.extraOptions)) {
return false;
}
return true;
Expand All @@ -56,13 +55,9 @@ const Section = (props: Props) => {
{(opt: FilterOption) => {
let subOpts: string[] | undefined;
let suboptions = opt.suboptions;
if (
opt.value === getFoundationNameLabel() &&
props.section?.value === FilterCategory.Maturity &&
!isUndefined(props.extraMaturity)
) {
suboptions = props.extraMaturity.options;
subOpts = props.extraMaturity.options.map((subOpt: FilterOption) => subOpt.value);
if (!isUndefined(props.extraOptions) && !isUndefined(props.extraOptions[opt.value])) {
suboptions = props.extraOptions[opt.value]!.options;
subOpts = props.extraOptions[opt.value]!.options.map((subOpt: FilterOption) => subOpt.value);
} else if (opt.suboptions) {
subOpts = opt.suboptions.map((subOpt: FilterOption) => subOpt.value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,13 @@
max-width: calc(100% - 1.5rem);
}

.goodFirstBadge {
height: 19px;
}

.goodFirstBadge img {
margin-top: -3px;
border: 1px solid var(--bs-gray-700);
max-height: 100%;
}

.badges {
Expand Down
Loading

0 comments on commit d0c9d79

Please sign in to comment.