Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/discover-checkbox-latest-only #1102

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add checkbox to show only latest versions
carlosallexandre committed Jan 18, 2025

Verified

This commit was signed with the committer’s verified signature.
renovate-bot Mend Renovate
commit b8de11fac2026b000b81d5c8c890d21112c68334
38 changes: 38 additions & 0 deletions eventcatalog/src/components/Checkbox.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
import type { HTMLAttributes } from 'astro/types';

export type Props = HTMLAttributes<'input'> & {
id: string;
name: string;
label: string;
};

const { id, name, label, class: className, ...attrs } = Astro.props;
---

<label for={id} class="relative block cursor-pointer select-none pl-5">
<input id={id} name={name} type="checkbox" class="h-0 w-0 cursor-pointer appearance-none group" {...attrs} />
<span class="text-md text-gray-700">{label}</span>
</label>

<style>
input::before {
@apply absolute left-0 top-1/2 block h-4 w-4 -translate-y-1/2 rounded-[4px] border border-gray-300;
content: '';
}

input:checked::before {
@apply bg-purple-600;
}

input:focus::before {
@apply outline outline-purple-300;
}

input:checked::after {
@apply absolute left-0 top-1/2 block h-4 w-4 -translate-y-1/2 bg-center bg-no-repeat;
content: '';
/** inline unicons checkmark SVG */
background-image: url("data:image/svg+xml;utf-8,<svg width='10' height='8' viewBox='0 0 10 8' fill='none' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M9.04731 1.01279C9.34958 1.296 9.36503 1.77062 9.08182 2.07289L4.19342 7.29032C3.77774 7.73398 3.07363 7.73398 2.65795 7.29032L1.01279 5.53443C0.729585 5.23216 0.745038 4.75754 1.04731 4.47433C1.34958 4.19112 1.8242 4.20658 2.1074 4.50884L3.42568 5.91586L7.98721 1.04731C8.27042 0.745037 8.74504 0.729585 9.04731 1.01279Z' fill='white'/></svg>");
}
</style>
30 changes: 28 additions & 2 deletions eventcatalog/src/components/Tables/Table.tsx
Original file line number Diff line number Diff line change
@@ -15,8 +15,9 @@ import type { CollectionEntry } from 'astro:content';
import DebouncedInput from './DebouncedInput';

import { getColumnsByCollection } from './columns';
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useState, type EventHandler } from 'react';
import type { CollectionTypes } from '@types';
import { isSameVersion } from '@utils/collections/util';

declare module '@tanstack/react-table' {
// @ts-ignore
@@ -32,10 +33,12 @@ export const Table = ({
data: initialData,
collection,
mode = 'simple',
checkboxLatestId,
}: {
data: CollectionEntry<'events'>[];
collection: string;
mode: 'simple' | 'full';
checkboxLatestId: string;
mode?: 'simple' | 'full';
}) => {
const [data, _setData] = useState(initialData);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
@@ -48,6 +51,20 @@ export const Table = ({
}
}, []);

const [showOnlyLatest, setShowOnlyLatest] = useState(true);

useEffect(() => {
const checkbox = document.getElementById(checkboxLatestId);

function handleChange(evt: Event) {
setShowOnlyLatest((evt.target as HTMLInputElement).checked);
}

checkbox?.addEventListener('change', handleChange);

return () => checkbox?.removeEventListener('change', handleChange);
}, [checkboxLatestId]);

const columns = useMemo(() => getColumnsByCollection(collection), [collection]);

const table = useReactTable({
@@ -63,6 +80,15 @@ export const Table = ({
getPaginationRowModel: getPaginationRowModel(),
state: {
columnFilters,
globalFilter: showOnlyLatest,
},
onGlobalFilterChange: setShowOnlyLatest,
globalFilterFn: (row, _columnId, showOnlyLatest: boolean) => {
if (showOnlyLatest) {
return isSameVersion(row.original.data.version, row.original.data.latestVersion);
}

return true;
},
});

11 changes: 8 additions & 3 deletions eventcatalog/src/layouts/DiscoverLayout.astro
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import { buildUrl } from '@utils/url-builder';
import { getQueries } from '@utils/queries';
import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
import Checkbox from '@components/Checkbox.astro';

const events = await getEvents();
const queries = await getQueries();
@@ -22,6 +23,8 @@ const flows = await getFlows();
const { title, subtitle, data, type } = Astro.props;
const currentPath = Astro.url.pathname;

const checkboxLatestId = 'latest-only';

const tabs = [
{
label: `Events (${events.length})`,
@@ -102,17 +105,19 @@ const tabs = [
<!-- Table -->
<div class="pb-20 ml-6 md:pr-10">
<div>
<div class="sm:flex sm:items-center py-4 pb-4" id="discover-title">
<div class="sm:flex sm:items-end py-4 pb-4" id="discover-title">
<div class="sm:flex-auto space-y-2">
<h1 class="text-4xl font-semibold text-gray-900 capitalize">{title}</h1>
<p class="text-md text-gray-700">{subtitle}</p>
</div>
<div class="p-4 border border-gray-200 rounded-md">
<Checkbox id={checkboxLatestId} name={checkboxLatestId} label="Show latest only" checked />
</div>
</div>
<div class="mt-4 flow-root">
<div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full align-middle sm:px-6 lg:px-8">
<!-- @ts-ignore -->
<Table data={data} collection={type} client:load />
<Table checkboxLatestId={checkboxLatestId} data={data} collection={type} client:load />
</div>
</div>
</div>
16 changes: 15 additions & 1 deletion eventcatalog/src/utils/__tests__/collections/util.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { satisfies, sortStringVersions } from '@utils/collections/util';
import { isSameVersion, satisfies, sortStringVersions } from '@utils/collections/util';
import { describe, it, expect } from 'vitest';

describe('Collections - utils', () => {
@@ -37,4 +37,18 @@ describe('Collections - utils', () => {
expect(sortStringVersions(versions)).toEqual({ versions: result, latestVersion: latest });
});
});

describe('isSameVersion', () => {
it.each([
[{ versions: ['1', '2'], result: false }],
[{ versions: ['1', '1'], result: true }],
[{ versions: ['2.0.0', '1.1.0'], result: false }],
[{ versions: ['2.0.0', '2.0.0'], result: true }],
[{ versions: ['a', 'b'], result: false }],
[{ versions: ['a', 'a'], result: true }],
[{ versions: ['1.0.0', undefined], result: false }],
])('should returns $result when versions is $versions', ({ versions, result }) => {
expect(isSameVersion(versions[0], versions[1])).toBe(result);
});
});
});
13 changes: 12 additions & 1 deletion eventcatalog/src/utils/collections/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CollectionTypes } from '@types';
import type { CollectionEntry } from 'astro:content';
import { coerce, compare, satisfies as satisfiesRange } from 'semver';
import { coerce, compare, eq, satisfies as satisfiesRange } from 'semver';

export const getPreviousVersion = (version: string, versions: string[]) => {
const index = versions.indexOf(version);
@@ -13,6 +13,17 @@ export const getVersions = (data: CollectionEntry<CollectionTypes>[]) => {
return sortStringVersions(versions);
};

export function isSameVersion(v1: string | undefined, v2: string | undefined) {
const semverV1 = coerce(v1);
const semverV2 = coerce(v2);

if (semverV1 != null && semverV2 != null) {
return eq(semverV1, semverV2);
}

return v1 === v2;
}

/**
* Sorts versioned items. Latest version first.
*/