Repository navigation
feat: colorful demo defaults for the sample dataset #78
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| import { describe, it, expect } from 'vitest'; | ||
| import { | ||
| computeSampleDefaults, | ||
| SAMPLE_CATEGORY_COLUMN, | ||
| } from '../utils/sampleDefaults'; | ||
| import type { SampleDefaultsContext } from '../utils/sampleDefaults'; | ||
|
|
||
| // The iris sample's string columns as detected by detectColumnTypes. | ||
| const IRIS_STRING_COLUMNS = ['species']; | ||
|
|
||
| const sampleContext = (overrides: Partial<SampleDefaultsContext> = {}): SampleDefaultsContext => ({ | ||
| isSample: true, | ||
| hasPendingViewState: false, | ||
| stringColumns: IRIS_STRING_COLUMNS, | ||
| ...overrides, | ||
| }); | ||
|
|
||
| describe('computeSampleDefaults', () => { | ||
| it('exports the expected sample category column', () => { | ||
| expect(SAMPLE_CATEGORY_COLUMN).toBe('species'); | ||
| }); | ||
|
|
||
| describe('sample dataset (normal case)', () => { | ||
| it('returns the full colorful defaults for the iris sample', () => { | ||
| expect(computeSampleDefaults(sampleContext())).toEqual({ | ||
| colorMode: 'category', | ||
| categoryColorColumn: 'species', | ||
| tintCellBorders: true, | ||
| showRegressionLine: true, | ||
| }); | ||
| }); | ||
|
|
||
| it('finds species among multiple string columns regardless of position', () => { | ||
| const result = computeSampleDefaults( | ||
| sampleContext({ stringColumns: ['label', 'species', 'notes'] }) | ||
| ); | ||
| expect(result?.colorMode).toBe('category'); | ||
| expect(result?.categoryColorColumn).toBe('species'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('non-sample loads keep neutral defaults', () => { | ||
| it('returns null for uploaded / history / URL data even with a species column', () => { | ||
| expect(computeSampleDefaults(sampleContext({ isSample: false }))).toBeNull(); | ||
| }); | ||
|
|
||
| it('returns null for non-sample data with no string columns', () => { | ||
| expect( | ||
| computeSampleDefaults(sampleContext({ isSample: false, stringColumns: [] })) | ||
| ).toBeNull(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('pending shared view state wins', () => { | ||
| it('returns null when a #view= state is pending, even for the sample', () => { | ||
| expect( | ||
| computeSampleDefaults(sampleContext({ hasPendingViewState: true })) | ||
| ).toBeNull(); | ||
| }); | ||
|
|
||
| it('returns null when both non-sample and pending view apply', () => { | ||
| expect( | ||
| computeSampleDefaults(sampleContext({ isSample: false, hasPendingViewState: true })) | ||
| ).toBeNull(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('sample without a species column (edge case)', () => { | ||
| it('still applies border tint and regression lines but not category coloring', () => { | ||
| expect(computeSampleDefaults(sampleContext({ stringColumns: [] }))).toEqual({ | ||
| colorMode: 'none', | ||
| categoryColorColumn: null, | ||
| tintCellBorders: true, | ||
| showRegressionLine: true, | ||
| }); | ||
| }); | ||
|
|
||
| it('does not fall back to a different string column', () => { | ||
| const result = computeSampleDefaults( | ||
| sampleContext({ stringColumns: ['variety', 'label'] }) | ||
| ); | ||
| expect(result?.colorMode).toBe('none'); | ||
| expect(result?.categoryColorColumn).toBeNull(); | ||
| }); | ||
|
|
||
| it('matches by exact name, not case-insensitively', () => { | ||
| const result = computeSampleDefaults(sampleContext({ stringColumns: ['Species'] })); | ||
| expect(result?.categoryColorColumn).toBeNull(); | ||
| expect(result?.colorMode).toBe('none'); | ||
| }); | ||
| }); | ||
|
|
||
| it('is pure: does not mutate the context it is given', () => { | ||
| const context = sampleContext(); | ||
| const stringColumnsBefore = [...context.stringColumns]; | ||
| computeSampleDefaults(context); | ||
| expect(context).toEqual(sampleContext()); | ||
| expect(context.stringColumns).toEqual(stringColumnsBefore); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import type { ColorMode } from '../../types'; | ||
|
|
||
| /** | ||
| * Demo defaults for the bundled sample dataset (iris). | ||
| * | ||
| * The sample data is most people's first impression of the app, so instead of | ||
| * the neutral monochrome defaults it ships with the colorful features turned | ||
| * on: category coloring by species, |r|-tinted cell borders, and per-cell | ||
| * regression lines. These are ordinary initial values — the user can toggle | ||
| * each one off afterwards exactly like normal. | ||
| * | ||
| * The decision of WHETHER (and with what values) to apply those defaults is | ||
| * kept here as a pure function so it can be tested exhaustively | ||
| * (PLAN.md regression guarantee (a)). | ||
| */ | ||
|
|
||
| /** The sample dataset's category column used for color-by. */ | ||
| export const SAMPLE_CATEGORY_COLUMN = 'species'; | ||
|
|
||
| export interface SampleDefaultsContext { | ||
| /** True only when the load came from the bundled sample dataset | ||
| * (auto-load on first visit or the "Load Sample Data" button). */ | ||
| isSample: boolean; | ||
| /** True when a shared #view= state is pending and about to be applied — | ||
| * the share link's settings must win over the demo defaults. */ | ||
| hasPendingViewState: boolean; | ||
| /** String (categorical) columns detected in the loaded data. */ | ||
| stringColumns: readonly string[]; | ||
| } | ||
|
|
||
| export interface SampleDefaults { | ||
| colorMode: ColorMode; | ||
| categoryColorColumn: string | null; | ||
| tintCellBorders: boolean; | ||
| showRegressionLine: boolean; | ||
| } | ||
|
|
||
| /** | ||
| * Decide the initial demo defaults for a completed data load. | ||
| * | ||
| * Returns the defaults to apply, or null when the load must keep the current | ||
| * neutral behavior (non-sample data, or a pending shared view that overrides | ||
| * everything). If the sample somehow lacks its category column, the | ||
| * non-categorical extras (border tint, regression lines) still apply but | ||
| * color mode is left at 'none'. | ||
| * | ||
| * Note: tinting cell borders does not require the correlation badges — | ||
| * ScatterPlotMatrix computes correlations whenever either flag is on | ||
| * (`correlationActive = showCorrelation || tintCellBorders`). | ||
| */ | ||
| export function computeSampleDefaults(context: SampleDefaultsContext): SampleDefaults | null { | ||
| if (!context.isSample) return null; | ||
| if (context.hasPendingViewState) return null; | ||
|
|
||
| const hasCategoryColumn = context.stringColumns.includes(SAMPLE_CATEGORY_COLUMN); | ||
| return { | ||
| colorMode: hasCategoryColumn ? 'category' : 'none', | ||
| categoryColorColumn: hasCategoryColumn ? SAMPLE_CATEGORY_COLUMN : null, | ||
| tintCellBorders: true, | ||
| showRegressionLine: true, | ||
| }; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Reset sample-only settings on subsequent non-sample loads.
When
computeSampleDefaultsreturnsnullfor an upload/history/URL load, the prior sample's enabled tint and regression settings remain active; category mode also remains active if the next dataset containsspecies. Explicitly restore these sample-controlled settings to neutral for non-sample loads, then let pending#view=state override them.Proposed fix
if (sampleDefaults) { setColorMode(sampleDefaults.colorMode); setCategoryColorColumn(sampleDefaults.categoryColorColumn); setTintCellBorders(sampleDefaults.tintCellBorders); setShowRegressionLine(sampleDefaults.showRegressionLine); + } else if (!isSample) { + setColorMode('none'); + setCategoryColorColumn(null); + setTintCellBorders(false); + setShowRegressionLine(false); }📝 Committable suggestion
🤖 Prompt for AI Agents