From d4f31b80479c82d45036be4b1703c0e469740e8f Mon Sep 17 00:00:00 2001 From: Rachel Shen Date: Tue, 25 Feb 2020 19:23:45 +0200 Subject: [PATCH] docs: enable story source for current stories (#557) This commit add the storySource addon to visualize the source code of each stories on the right panel Co-authored-by: Marco Vettorello --- .eslintrc.js | 1 + .playground/playground.tsx | 10 +- .storybook/addons.ts | 1 + .storybook/config.ts | 4 +- .storybook/webpack.config.js | 13 + docs/charts.tsx | 26 +- integration/helpers.ts | 2 +- ...stogram-visually-looks-correct-1-snap.png} | Bin ...styling-visually-looks-correct-1-snap.png} | Bin ...-domain-visually-looks-correct-1-snap.png} | Bin ...-domain-visually-looks-correct-1-snap.png} | Bin ...-domain-visually-looks-correct-1-snap.png} | Bin ...-domain-visually-looks-correct-1-snap.png} | Bin ...r-chart-visually-looks-correct-1-snap.png} | Bin ...e-chart-visually-looks-correct-1-snap.png} | Bin ...r-chart-visually-looks-correct-1-snap.png} | Bin ...styling-visually-looks-correct-1-snap.png} | Bin ...ibility-visually-looks-correct-1-snap.png} | Bin ...nd-area-visually-looks-correct-1-snap.png} | Bin ...ed-band-visually-looks-correct-1-snap.png} | Bin ...grouped-visually-looks-correct-1-snap.png} | Bin ...centage-visually-looks-correct-1-snap.png} | Bin ...-naming-visually-looks-correct-1-snap.png} | Bin ...e-specs-visually-looks-correct-1-snap.png} | Bin ...stacked-visually-looks-correct-1-snap.png} | Bin ...-legend-visually-looks-correct-1-snap.png} | Bin ...d-lines-visually-looks-correct-1-snap.png} | Bin ...s-basic-visually-looks-correct-1-snap.png} | Bin ...-domain-visually-looks-correct-1-snap.png} | Bin ...m-mixed-visually-looks-correct-1-snap.png} | Bin ...tooltip-visually-looks-correct-1-snap.png} | Bin ...-domain-visually-looks-correct-1-snap.png} | Bin ...-labels-visually-looks-correct-1-snap.png} | Bin ...ti-axes-visually-looks-correct-1-snap.png} | Bin ...n-bound-visually-looks-correct-1-snap.png} | Bin ...otation-visually-looks-correct-1-snap.png} | Bin ...-4-axes-visually-looks-correct-1-snap.png} | Bin ...o-extent-visually-looks-correct-1-snap.png | Bin 0 -> 12561 bytes ...-legend-visually-looks-correct-1-snap.png} | Bin ...th-axis-visually-looks-correct-1-snap.png} | Bin ...-values-visually-looks-correct-1-snap.png} | Bin ...-legend-visually-looks-correct-1-snap.png} | Bin ...th-axis-visually-looks-correct-1-snap.png} | Bin ...-legend-visually-looks-correct-1-snap.png} | Bin ...-legend-visually-looks-correct-1-snap.png} | Bin ...th-axis-visually-looks-correct-1-snap.png} | Bin ...te-axis-visually-looks-correct-1-snap.png} | Bin ...imezone-visually-looks-correct-1-snap.png} | Bin ...uration-visually-looks-correct-1-snap.png} | Bin ...imezone-visually-looks-correct-1-snap.png} | Bin ...-in-utc-visually-looks-correct-1-snap.png} | Bin ...art-size-visually-looks-correct-1-snap.png | Bin 1167 -> 1154 bytes ...-config-visually-looks-correct-1-snap.png} | Bin ...matting-visually-looks-correct-1-snap.png} | Bin ...es-name-visually-looks-correct-1-snap.png} | Bin ...-styling-visually-looks-correct-1-snap.png | Bin 0 -> 53539 bytes ...-slices-visually-looks-correct-1-snap.png} | Bin ...-slices-visually-looks-correct-1-snap.png} | Bin ...-slices-visually-looks-correct-1-snap.png} | Bin ...t-basic-visually-looks-correct-1-snap.png} | Bin ...r-empty-visually-looks-correct-1-snap.png} | Bin ...egative-visually-looks-correct-1-snap.png} | Bin ...o-slice-visually-looks-correct-1-snap.png} | Bin ...e-slice-visually-looks-correct-1-snap.png} | Bin ...ll-sice-visually-looks-correct-1-snap.png} | Bin ...y-small-visually-looks-correct-1-snap.png} | Bin ...-layers-visually-looks-correct-1-snap.png} | Bin ...-layers-visually-looks-correct-1-snap.png} | Bin ...al-zero-visually-looks-correct-1-snap.png} | Bin ...rmatted-visually-looks-correct-1-snap.png} | Bin ...palette-visually-looks-correct-1-snap.png} | Bin ...d-small-visually-looks-correct-1-snap.png} | Bin ...-labels-visually-looks-correct-1-snap.png} | Bin ...-labels-visually-looks-correct-1-snap.png} | Bin ...-slices-visually-looks-correct-1-snap.png} | Bin ...-render-with-domain-constraints-1-snap.png | Bin 11634 -> 26134 bytes integration/tests/annotations_stories.test.ts | 8 +- integration/tests/area_stories.test.ts | 12 +- integration/tests/axis_stories.test.ts | 18 +- integration/tests/line_stories.test.ts | 8 +- package.json | 1 + src/components/chart.test.tsx | 2 +- src/components/chart_status.tsx | 4 +- src/components/legend/legend.test.tsx | 4 +- src/specs/specs_parser.test.tsx | 10 +- stories/annotations.tsx | 642 ------ stories/annotations/lines/1_x_continuous.tsx | 71 + stories/annotations/lines/2_x_ordinal.tsx | 52 + stories/annotations/lines/3_x_time.tsx | 62 + stories/annotations/lines/4_y_domain.tsx | 58 + stories/annotations/lines/5_styling.tsx | 88 + .../lines/6_test_single_bar_histogram.tsx | 58 + stories/annotations/lines/line.stories.tsx | 16 + .../annotations/rects/1_linear_bar_chart.tsx | 43 + .../annotations/rects/2_ordinal_bar_chart.tsx | 42 + .../annotations/rects/3_linear_line_chart.tsx | 81 + stories/annotations/rects/4_styling.tsx | 111 + .../rects/5_tooltip_visibility.tsx | 64 + stories/annotations/rects/rects.stories.tsx | 14 + stories/area/10_stacked_same_naming.tsx | 64 + stories/area/11_test_linear.tsx | 45 + stories/area/12_test_time.tsx | 49 + stories/area/13_band_area.tsx | 77 + stories/area/14_stacked_band.tsx | 55 + stories/area/15_stacked_grouped.tsx | 87 + stories/area/1_basic.tsx | 27 + stories/area/2_with_time.tsx | 42 + stories/area/3_with_linear.tsx | 39 + stories/area/4_with_log.tsx | 40 + stories/area/5_with_4_axes.tsx | 50 + stories/area/6_with_axis_and_legend.tsx | 43 + stories/area/7_stacked.tsx | 54 + stories/area/8_stacked_percentage.tsx | 40 + stories/area/9_stacked_separate_specs.tsx | 64 + stories/area/area.stories.tsx | 25 + stories/area_chart.tsx | 683 ------ stories/axes/10_one_domain_bound.tsx | 41 + stories/axes/11_fit_domain_extent.tsx | 53 + stories/axes/1_basic.tsx | 70 + stories/axes/2_tick_label_rotation.tsx | 85 + stories/axes/3_axis_4_axes.tsx | 53 + stories/axes/4_multi_axis.tsx | 94 + stories/axes/5_multi_axis_bar_lines.tsx | 57 + stories/axes/6_different_tooltip.tsx | 66 + stories/axes/7_many_tick_labels.tsx | 34 + stories/axes/8_custom_domain.tsx | 85 + stories/axes/9_custom_mixed_domain.tsx | 77 + stories/axes/axes.stories.tsx | 20 + stories/axis.tsx | 714 ------ stories/bar/10_axis_and_legend.tsx | 36 + .../bar/11_stacked_with_axis_and_legend.tsx | 41 + stories/bar/12_stacked_as_percentage.tsx | 50 + stories/bar/13_clustered.tsx | 57 + stories/bar/14_clustered_multiple.tsx | 66 + stories/bar/15_time_clustered.tsx | 48 + stories/bar/17_time_stacked.tsx | 51 + stories/bar/18_bar_chart_1y0g.tsx | 31 + stories/bar/19_bar_chart_1y1g.tsx | 32 + stories/bar/1_basic.tsx | 32 + stories/bar/20_bar_chart_1y2g.tsx | 32 + stories/bar/21_bar_chart_2y0g.tsx | 31 + stories/bar/22_barchart_2y1g.tsx | 32 + stories/bar/23_bar_chart_2y2g.tsx | 40 + stories/bar/24_tooltip_visibility.tsx | 37 + stories/bar/25_high_data_volume.tsx | 37 + stories/bar/26_single_data_linear.tsx | 42 + stories/bar/27_single_data_ordinal.tsx | 37 + stories/bar/28_single_data_clustered.tsx | 35 + stories/bar/29_single_data_stacked.tsx | 36 + stories/bar/2_label_value.tsx | 106 + stories/bar/30_stacked_to_extent.tsx | 36 + .../bar/31_negative_and_positive_x_values.tsx | 39 + stories/bar/32_scale_to_extent.tsx | 55 + stories/bar/33_band_bar.tsx | 63 + stories/bar/34_test_linear.tsx | 46 + stories/bar/35_test_time.tsx | 50 + stories/bar/36_test_linear_clustered.tsx | 46 + stories/bar/37_test_time_clustered.tsx | 50 + stories/bar/38_test_clustered_null_bars.tsx | 46 + stories/bar/39_test_stacked_null.tsx | 47 + stories/bar/3_with_axis.tsx | 31 + stories/bar/40_test_switch.tsx | 43 + stories/bar/41_test_histogram_linear.tsx | 158 ++ stories/bar/42_test_histogram_ordinal.tsx | 72 + stories/bar/43_test_discover.tsx | 58 + stories/bar/44_test_single_histogram.tsx | 52 + stories/bar/45_min_height.tsx | 42 + stories/bar/46_test_min_height.tsx | 52 + stories/bar/47_stacked_only_grouped.tsx | 143 ++ stories/bar/48_test_tooltip.tsx | 34 + stories/bar/4_ordinal.tsx | 34 + stories/bar/5_linear.tsx | 46 + stories/bar/6_linear_no_linear_interval.tsx | 36 + stories/bar/7_with_time_xaxis.tsx | 32 + stories/bar/8_with_log_yaxis.tsx | 40 + stories/bar/9_with_stacked_log.tsx | 49 + stories/bar/bars.stories.tsx | 61 + stories/bar_chart.tsx | 1992 ----------------- stories/{grid.tsx => grids/1_basic.tsx} | 112 +- stories/grids/2_multiple_axes.tsx | 114 + stories/grids/grids.stories.tsx | 11 + stories/interactions.tsx | 933 -------- .../interactions/10_brush_selection_bar.tsx | 30 + stories/interactions/11_brush_time.tsx | 58 + stories/interactions/12_brush_time_hist.tsx | 44 + .../13_brush_disabled_ordinal.tsx | 28 + stories/interactions/14_crosshair_time.tsx | 105 + stories/interactions/15_render_change.tsx | 37 + .../interactions/16_cursor_update_action.tsx | 36 + stories/interactions/17_png_export.tsx | 74 + stories/interactions/1_bar_clicks.tsx | 56 + stories/interactions/2_area_point_clicks.tsx | 33 + stories/interactions/3_line_point_clicks.tsx | 33 + .../interactions/4_line_area_bar_clicks.tsx | 60 + .../5_clicks_legend_items_bar.tsx | 83 + .../6_clicks_legend_items_area.tsx | 40 + .../7_clicks_legend_items_line.tsx | 105 + .../8_clicks_legend_items_mixed.tsx | 49 + .../interactions/9_brush_selection_linear.tsx | 31 + stories/interactions/interactions.stories.tsx | 27 + stories/legend.tsx | 339 --- stories/legend/1_legend_right.tsx | 35 + stories/legend/2_legend_bottom.tsx | 31 + stories/legend/3_legend_left.tsx | 31 + stories/legend/4_legend_top.tsx | 31 + stories/legend/5_changing_specs.tsx | 25 + stories/legend/6_hide_legend.tsx | 47 + stories/legend/7_display_values.tsx | 53 + stories/legend/8_spacing_buffer.tsx | 46 + stories/legend/legend.stories.tsx | 17 + stories/line/1_basic.tsx | 25 + stories/line/2_w_axis.tsx | 35 + stories/line/3_ordinal.tsx | 37 + stories/line/4_linear.tsx | 27 + stories/line/5_w_axis_and_legend.tsx | 28 + stories/line/6_curved.tsx | 85 + stories/line/7_multiple.tsx | 58 + stories/line/8_stacked.tsx | 60 + stories/line/9_multi_series.tsx | 45 + stories/line/line.stories.tsx | 18 + stories/line_chart.tsx | 394 ---- stories/mixed.tsx | 481 ---- stories/mixed/1_bars_and_lines.tsx | 40 + stories/mixed/2_lines_and_areas.tsx | 41 + stories/mixed/3_areas_and_bars.tsx | 46 + stories/mixed/4_test_bar.tsx | 53 + stories/mixed/5_test_bar_time.tsx | 63 + stories/mixed/6_fitting.tsx | 192 ++ stories/mixed/mixed.stories.tsx | 15 + stories/rotations.tsx | 299 --- stories/rotations/1_ordinal.tsx | 72 + stories/rotations/2_negative_ordinal.tsx | 27 + stories/rotations/3_rotations_ordinal.tsx | 27 + stories/rotations/4_90_ordinal.tsx | 27 + stories/rotations/5_180_ordinal.tsx | 27 + stories/rotations/6_negative_linear.tsx | 27 + stories/rotations/7_rotations_linear.tsx | 27 + stories/rotations/8_90_deg_linear.tsx | 27 + stories/rotations/9_180_deg_linear.tsx | 27 + stories/rotations/rotations.stories.tsx | 18 + stories/scales.tsx | 258 --- stories/scales/1_different_timezones.tsx | 95 + stories/scales/2_local_tooltip.tsx | 45 + stories/scales/3_utc_tooltip.tsx | 47 + stories/scales/4_specified_timezone.tsx | 45 + stories/scales/5_remove_duplicates.tsx | 39 + stories/scales/scales.stories.tsx | 14 + stories/styling.tsx | 1226 ---------- stories/stylings/10_custom_bars.tsx | 78 + stories/stylings/11_custom_lines.tsx | 102 + stories/stylings/12_custom_area.tsx | 133 ++ stories/stylings/13_custom_series_name.tsx | 43 + .../stylings/13_custom_series_name_config.tsx | 50 + .../14_custom_series_name_formatting.tsx | 72 + stories/stylings/15_tick_label.tsx | 61 + stories/stylings/16_style_accessor.tsx | 96 + stories/stylings/1_chart_size.tsx | 85 + stories/stylings/2_margins.tsx | 94 + stories/stylings/3_axis.tsx | 73 + stories/stylings/4_theme_styling.tsx | 182 ++ stories/stylings/5_partial_custom_theme.tsx | 39 + stories/stylings/6_partial_and_base.tsx | 45 + stories/stylings/7_multiple_custom.tsx | 60 + .../stylings/8_custom_series_colors_array.tsx | 33 + .../9_custom_series_colors_function.tsx | 64 + stories/stylings/stylings.stories.tsx | 27 + stories/sunburst.tsx | 833 ------- stories/sunburst/10_2_slice.tsx | 27 + stories/sunburst/11_small_large.tsx | 34 + stories/sunburst/12_very_small.tsx | 29 + stories/sunburst/13_empty.tsx | 29 + stories/sunburst/14_full_zero.tsx | 29 + stories/sunburst/15_single.tsx | 27 + stories/sunburst/16_single_small.tsx | 27 + stories/sunburst/17_single_very_small.tsx | 27 + stories/sunburst/18_no_sliced.tsx | 26 + stories/sunburst/19_negative.tsx | 30 + stories/sunburst/1_simple.tsx | 26 + stories/sunburst/20_total_zero.tsx | 27 + stories/sunburst/21_high_pie.tsx | 30 + stories/sunburst/22_counter_clockwise.tsx | 30 + stories/sunburst/23_clockwise.tsx | 30 + stories/sunburst/24_linked_label.tsx | 30 + stories/sunburst/25_no_labels.tsx | 30 + stories/sunburst/2_value_formatted.tsx | 36 + stories/sunburst/3_value_formatted_2.tsx | 37 + stories/sunburst/4_fill_labels.tsx | 45 + stories/sunburst/5_donut.tsx | 45 + stories/sunburst/6_pie_chart_labels.tsx | 29 + stories/sunburst/7_zero_slice.tsx | 30 + stories/sunburst/8_sunburst_two_layers.tsx | 62 + stories/sunburst/9_sunburst_three_layers.tsx | 78 + stories/sunburst/sunburst.stories.tsx | 36 + stories/treemap.tsx | 345 --- stories/treemap/1_one_layer.tsx | 41 + stories/treemap/2_one_layer_2.tsx | 40 + stories/treemap/3_mid_two.tsx | 74 + stories/treemap/4_two_layer_stress.tsx | 84 + stories/treemap/5_multicolor.tsx | 68 + stories/treemap/6_custom_style.tsx | 65 + stories/treemap/treemap.stories.tsx | 15 + stories/{common.ts => utils/knobs.ts} | 2 +- stories/utils/storybook.ts | 3 + wiki/consuming.md | 2 +- wiki/overview.md | 10 +- yarn.lock | 155 ++ 306 files changed, 10355 insertions(+), 9297 deletions(-) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-test-line-annotation-single-value-histogram-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-lines-single-bar-histogram-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-line-styling-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-lines-styling-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-line-basic-x-domain-continous-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-lines-x-continuous-domain-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-line-basic-x-domain-ordinal-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-lines-x-ordinal-domain-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-line-time-series-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-lines-x-time-domain-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-line-basic-y-domain-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-lines-y-domain-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-rect-basic-annotation-linear-bar-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-rects-linear-bar-chart-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-rect-basic-annotation-line-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-rects-linear-line-chart-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-rect-basic-annotation-ordinal-bar-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-rects-ordinal-bar-chart-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-rect-styling-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-rects-styling-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-annotations-rect-tooltip-visilibity-dependent-on-content-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-annotations-rects-tooltip-visibility-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-band-area-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-band-area-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-band-area-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-band-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-only-grouped-areas-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-grouped-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-as-percentage-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-percentage-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-with-separated-specs-same-naming-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-same-naming-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-w-axis-and-legend-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-separate-specs-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-with-separated-specs-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-stacked-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-area-chart-w-axis-and-legend-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-area-chart-with-axis-and-legend-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-with-multi-axis-bar-lines-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-bars-and-lines-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-basic-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-basic-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-customizing-domain-limits-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-custom-domain-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-customizing-domain-limits-mixed-ordinal-linear-x-domain-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-custom-mixed-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-with-multi-axis-different-tooltip-formats-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-different-tooltip-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-fit-domain-to-extent-in-y-axis-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-fit-domain-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-w-many-tick-labels-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-many-tick-labels-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-with-multi-axis-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-multi-axes-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-customizing-domain-limits-only-one-bound-defined-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-one-domain-bound-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-tick-label-rotation-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-tick-label-rotation-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-axis-axis-4-axes-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-axes-with-4-axes-visually-looks-correct-1-snap.png} (100%) create mode 100644 integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-bar-chart-stacked-to-extent-visually-looks-correct-1-snap.png rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-curvedwaxisandlegend-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-curved-with-axis-and-legend-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-linear-w-axis-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-linear-with-axis-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiserieswithlogvalues-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multi-series-with-log-values-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiplewaxisandlegend-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiple-with-axis-and-legend-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-ordinal-w-axis-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-ordinal-with-axis-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-stackedwaxisandlegend-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-stacked-with-axis-and-legend-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-w-axis-and-legend-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-with-axis-and-legend-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-line-chart-w-axis-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-line-chart-with-axis-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-scales-remove-duplicate-scales-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-scales-remove-duplicate-axis-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-year-scale-custom-timezone-same-tone-tooltip-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-scales-specified-timezone-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-scales-line-chart-with-different-timezones-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-scales-timezone-configuration-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-utc-timezone-local-tooltip-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-scales-tooltip-in-local-timezone-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-utc-timezone-utc-tooltip-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-scales-tooltip-in-utc-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-naming-config-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-config-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-series-name-formatting-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-formatting-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-series-name-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-visually-looks-correct-1-snap.png} (100%) create mode 100644 integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-theme-styling-visually-looks-correct-1-snap.png rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-full-zero-slice-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-full-and-zero-slices-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slice-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slices-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-large-small-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-large-and-small-slices-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-simple-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-big-empty-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-near-full-near-empty-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-no-pie-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-no-slice-no-pie-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-no-slice-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-slice-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-slice-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-small-slice-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-small-sice-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-very-small-slice-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-very-small-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-three-layers-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-two-layers-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-total-zero-no-pie-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-total-zero-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-pie-chart-2-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-very-large-small-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-very-large-and-small-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-pie-chart-labels-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-direct-text-labels-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-pie-chart-with-fill-labels-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png} (100%) rename integration/tests/__image_snapshots__/{all-test-ts-baseline-visual-tests-for-all-stories-sunburst-two-slices-pie-chart-visually-looks-correct-1-snap.png => all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-two-slices-visually-looks-correct-1-snap.png} (100%) delete mode 100644 stories/annotations.tsx create mode 100644 stories/annotations/lines/1_x_continuous.tsx create mode 100644 stories/annotations/lines/2_x_ordinal.tsx create mode 100644 stories/annotations/lines/3_x_time.tsx create mode 100644 stories/annotations/lines/4_y_domain.tsx create mode 100644 stories/annotations/lines/5_styling.tsx create mode 100644 stories/annotations/lines/6_test_single_bar_histogram.tsx create mode 100644 stories/annotations/lines/line.stories.tsx create mode 100644 stories/annotations/rects/1_linear_bar_chart.tsx create mode 100644 stories/annotations/rects/2_ordinal_bar_chart.tsx create mode 100644 stories/annotations/rects/3_linear_line_chart.tsx create mode 100644 stories/annotations/rects/4_styling.tsx create mode 100644 stories/annotations/rects/5_tooltip_visibility.tsx create mode 100644 stories/annotations/rects/rects.stories.tsx create mode 100644 stories/area/10_stacked_same_naming.tsx create mode 100644 stories/area/11_test_linear.tsx create mode 100644 stories/area/12_test_time.tsx create mode 100644 stories/area/13_band_area.tsx create mode 100644 stories/area/14_stacked_band.tsx create mode 100644 stories/area/15_stacked_grouped.tsx create mode 100644 stories/area/1_basic.tsx create mode 100644 stories/area/2_with_time.tsx create mode 100644 stories/area/3_with_linear.tsx create mode 100644 stories/area/4_with_log.tsx create mode 100644 stories/area/5_with_4_axes.tsx create mode 100644 stories/area/6_with_axis_and_legend.tsx create mode 100644 stories/area/7_stacked.tsx create mode 100644 stories/area/8_stacked_percentage.tsx create mode 100644 stories/area/9_stacked_separate_specs.tsx create mode 100644 stories/area/area.stories.tsx delete mode 100644 stories/area_chart.tsx create mode 100644 stories/axes/10_one_domain_bound.tsx create mode 100644 stories/axes/11_fit_domain_extent.tsx create mode 100644 stories/axes/1_basic.tsx create mode 100644 stories/axes/2_tick_label_rotation.tsx create mode 100644 stories/axes/3_axis_4_axes.tsx create mode 100644 stories/axes/4_multi_axis.tsx create mode 100644 stories/axes/5_multi_axis_bar_lines.tsx create mode 100644 stories/axes/6_different_tooltip.tsx create mode 100644 stories/axes/7_many_tick_labels.tsx create mode 100644 stories/axes/8_custom_domain.tsx create mode 100644 stories/axes/9_custom_mixed_domain.tsx create mode 100644 stories/axes/axes.stories.tsx delete mode 100644 stories/axis.tsx create mode 100644 stories/bar/10_axis_and_legend.tsx create mode 100644 stories/bar/11_stacked_with_axis_and_legend.tsx create mode 100644 stories/bar/12_stacked_as_percentage.tsx create mode 100644 stories/bar/13_clustered.tsx create mode 100644 stories/bar/14_clustered_multiple.tsx create mode 100644 stories/bar/15_time_clustered.tsx create mode 100644 stories/bar/17_time_stacked.tsx create mode 100644 stories/bar/18_bar_chart_1y0g.tsx create mode 100644 stories/bar/19_bar_chart_1y1g.tsx create mode 100644 stories/bar/1_basic.tsx create mode 100644 stories/bar/20_bar_chart_1y2g.tsx create mode 100644 stories/bar/21_bar_chart_2y0g.tsx create mode 100644 stories/bar/22_barchart_2y1g.tsx create mode 100644 stories/bar/23_bar_chart_2y2g.tsx create mode 100644 stories/bar/24_tooltip_visibility.tsx create mode 100644 stories/bar/25_high_data_volume.tsx create mode 100644 stories/bar/26_single_data_linear.tsx create mode 100644 stories/bar/27_single_data_ordinal.tsx create mode 100644 stories/bar/28_single_data_clustered.tsx create mode 100644 stories/bar/29_single_data_stacked.tsx create mode 100644 stories/bar/2_label_value.tsx create mode 100644 stories/bar/30_stacked_to_extent.tsx create mode 100644 stories/bar/31_negative_and_positive_x_values.tsx create mode 100644 stories/bar/32_scale_to_extent.tsx create mode 100644 stories/bar/33_band_bar.tsx create mode 100644 stories/bar/34_test_linear.tsx create mode 100644 stories/bar/35_test_time.tsx create mode 100644 stories/bar/36_test_linear_clustered.tsx create mode 100644 stories/bar/37_test_time_clustered.tsx create mode 100644 stories/bar/38_test_clustered_null_bars.tsx create mode 100644 stories/bar/39_test_stacked_null.tsx create mode 100644 stories/bar/3_with_axis.tsx create mode 100644 stories/bar/40_test_switch.tsx create mode 100644 stories/bar/41_test_histogram_linear.tsx create mode 100644 stories/bar/42_test_histogram_ordinal.tsx create mode 100644 stories/bar/43_test_discover.tsx create mode 100644 stories/bar/44_test_single_histogram.tsx create mode 100644 stories/bar/45_min_height.tsx create mode 100644 stories/bar/46_test_min_height.tsx create mode 100644 stories/bar/47_stacked_only_grouped.tsx create mode 100644 stories/bar/48_test_tooltip.tsx create mode 100644 stories/bar/4_ordinal.tsx create mode 100644 stories/bar/5_linear.tsx create mode 100644 stories/bar/6_linear_no_linear_interval.tsx create mode 100644 stories/bar/7_with_time_xaxis.tsx create mode 100644 stories/bar/8_with_log_yaxis.tsx create mode 100644 stories/bar/9_with_stacked_log.tsx create mode 100644 stories/bar/bars.stories.tsx delete mode 100644 stories/bar_chart.tsx rename stories/{grid.tsx => grids/1_basic.tsx} (62%) create mode 100644 stories/grids/2_multiple_axes.tsx create mode 100644 stories/grids/grids.stories.tsx delete mode 100644 stories/interactions.tsx create mode 100644 stories/interactions/10_brush_selection_bar.tsx create mode 100644 stories/interactions/11_brush_time.tsx create mode 100644 stories/interactions/12_brush_time_hist.tsx create mode 100644 stories/interactions/13_brush_disabled_ordinal.tsx create mode 100644 stories/interactions/14_crosshair_time.tsx create mode 100644 stories/interactions/15_render_change.tsx create mode 100644 stories/interactions/16_cursor_update_action.tsx create mode 100644 stories/interactions/17_png_export.tsx create mode 100644 stories/interactions/1_bar_clicks.tsx create mode 100644 stories/interactions/2_area_point_clicks.tsx create mode 100644 stories/interactions/3_line_point_clicks.tsx create mode 100644 stories/interactions/4_line_area_bar_clicks.tsx create mode 100644 stories/interactions/5_clicks_legend_items_bar.tsx create mode 100644 stories/interactions/6_clicks_legend_items_area.tsx create mode 100644 stories/interactions/7_clicks_legend_items_line.tsx create mode 100644 stories/interactions/8_clicks_legend_items_mixed.tsx create mode 100644 stories/interactions/9_brush_selection_linear.tsx create mode 100644 stories/interactions/interactions.stories.tsx delete mode 100644 stories/legend.tsx create mode 100644 stories/legend/1_legend_right.tsx create mode 100644 stories/legend/2_legend_bottom.tsx create mode 100644 stories/legend/3_legend_left.tsx create mode 100644 stories/legend/4_legend_top.tsx create mode 100644 stories/legend/5_changing_specs.tsx create mode 100644 stories/legend/6_hide_legend.tsx create mode 100644 stories/legend/7_display_values.tsx create mode 100644 stories/legend/8_spacing_buffer.tsx create mode 100644 stories/legend/legend.stories.tsx create mode 100644 stories/line/1_basic.tsx create mode 100644 stories/line/2_w_axis.tsx create mode 100644 stories/line/3_ordinal.tsx create mode 100644 stories/line/4_linear.tsx create mode 100644 stories/line/5_w_axis_and_legend.tsx create mode 100644 stories/line/6_curved.tsx create mode 100644 stories/line/7_multiple.tsx create mode 100644 stories/line/8_stacked.tsx create mode 100644 stories/line/9_multi_series.tsx create mode 100644 stories/line/line.stories.tsx delete mode 100644 stories/line_chart.tsx delete mode 100644 stories/mixed.tsx create mode 100644 stories/mixed/1_bars_and_lines.tsx create mode 100644 stories/mixed/2_lines_and_areas.tsx create mode 100644 stories/mixed/3_areas_and_bars.tsx create mode 100644 stories/mixed/4_test_bar.tsx create mode 100644 stories/mixed/5_test_bar_time.tsx create mode 100644 stories/mixed/6_fitting.tsx create mode 100644 stories/mixed/mixed.stories.tsx delete mode 100644 stories/rotations.tsx create mode 100644 stories/rotations/1_ordinal.tsx create mode 100644 stories/rotations/2_negative_ordinal.tsx create mode 100644 stories/rotations/3_rotations_ordinal.tsx create mode 100644 stories/rotations/4_90_ordinal.tsx create mode 100644 stories/rotations/5_180_ordinal.tsx create mode 100644 stories/rotations/6_negative_linear.tsx create mode 100644 stories/rotations/7_rotations_linear.tsx create mode 100644 stories/rotations/8_90_deg_linear.tsx create mode 100644 stories/rotations/9_180_deg_linear.tsx create mode 100644 stories/rotations/rotations.stories.tsx delete mode 100644 stories/scales.tsx create mode 100644 stories/scales/1_different_timezones.tsx create mode 100644 stories/scales/2_local_tooltip.tsx create mode 100644 stories/scales/3_utc_tooltip.tsx create mode 100644 stories/scales/4_specified_timezone.tsx create mode 100644 stories/scales/5_remove_duplicates.tsx create mode 100644 stories/scales/scales.stories.tsx delete mode 100644 stories/styling.tsx create mode 100644 stories/stylings/10_custom_bars.tsx create mode 100644 stories/stylings/11_custom_lines.tsx create mode 100644 stories/stylings/12_custom_area.tsx create mode 100644 stories/stylings/13_custom_series_name.tsx create mode 100644 stories/stylings/13_custom_series_name_config.tsx create mode 100644 stories/stylings/14_custom_series_name_formatting.tsx create mode 100644 stories/stylings/15_tick_label.tsx create mode 100644 stories/stylings/16_style_accessor.tsx create mode 100644 stories/stylings/1_chart_size.tsx create mode 100644 stories/stylings/2_margins.tsx create mode 100644 stories/stylings/3_axis.tsx create mode 100644 stories/stylings/4_theme_styling.tsx create mode 100644 stories/stylings/5_partial_custom_theme.tsx create mode 100644 stories/stylings/6_partial_and_base.tsx create mode 100644 stories/stylings/7_multiple_custom.tsx create mode 100644 stories/stylings/8_custom_series_colors_array.tsx create mode 100644 stories/stylings/9_custom_series_colors_function.tsx create mode 100644 stories/stylings/stylings.stories.tsx delete mode 100644 stories/sunburst.tsx create mode 100644 stories/sunburst/10_2_slice.tsx create mode 100644 stories/sunburst/11_small_large.tsx create mode 100644 stories/sunburst/12_very_small.tsx create mode 100644 stories/sunburst/13_empty.tsx create mode 100644 stories/sunburst/14_full_zero.tsx create mode 100644 stories/sunburst/15_single.tsx create mode 100644 stories/sunburst/16_single_small.tsx create mode 100644 stories/sunburst/17_single_very_small.tsx create mode 100644 stories/sunburst/18_no_sliced.tsx create mode 100644 stories/sunburst/19_negative.tsx create mode 100644 stories/sunburst/1_simple.tsx create mode 100644 stories/sunburst/20_total_zero.tsx create mode 100644 stories/sunburst/21_high_pie.tsx create mode 100644 stories/sunburst/22_counter_clockwise.tsx create mode 100644 stories/sunburst/23_clockwise.tsx create mode 100644 stories/sunburst/24_linked_label.tsx create mode 100644 stories/sunburst/25_no_labels.tsx create mode 100644 stories/sunburst/2_value_formatted.tsx create mode 100644 stories/sunburst/3_value_formatted_2.tsx create mode 100644 stories/sunburst/4_fill_labels.tsx create mode 100644 stories/sunburst/5_donut.tsx create mode 100644 stories/sunburst/6_pie_chart_labels.tsx create mode 100644 stories/sunburst/7_zero_slice.tsx create mode 100644 stories/sunburst/8_sunburst_two_layers.tsx create mode 100644 stories/sunburst/9_sunburst_three_layers.tsx create mode 100644 stories/sunburst/sunburst.stories.tsx delete mode 100644 stories/treemap.tsx create mode 100644 stories/treemap/1_one_layer.tsx create mode 100644 stories/treemap/2_one_layer_2.tsx create mode 100644 stories/treemap/3_mid_two.tsx create mode 100644 stories/treemap/4_two_layer_stress.tsx create mode 100644 stories/treemap/5_multicolor.tsx create mode 100644 stories/treemap/6_custom_style.tsx create mode 100644 stories/treemap/treemap.stories.tsx rename stories/{common.ts => utils/knobs.ts} (95%) create mode 100644 stories/utils/storybook.ts diff --git a/.eslintrc.js b/.eslintrc.js index dfef52698b..14770614e0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -68,6 +68,7 @@ module.exports = { '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/ban-ts-ignore': 'off', '@typescript-eslint/no-inferrable-types': 'off', + 'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }], }, settings: { 'import/resolver': { diff --git a/.playground/playground.tsx b/.playground/playground.tsx index 4c235f1933..0201be20ce 100644 --- a/.playground/playground.tsx +++ b/.playground/playground.tsx @@ -15,13 +15,13 @@ export class Playground extends React.Component<{}, { isSunburstShown: boolean } <>
- - - + + + { exclude: /node_modules/, }); + config.module.rules.push({ + test: /\.tsx?$/, + include: [path.resolve(__dirname, '../stories/')], + exclude: [path.resolve(__dirname, '../stories/utils')], + loaders: [ + { + loader: require.resolve('@storybook/source-loader'), + options: { parser: 'typescript' }, + }, + ], + enforce: 'pre', + }); + // Replace default css rules with nonce config.module.rules = config.module.rules.filter(({ test }) => !test.test('.css')); config.module.rules.push({ diff --git a/docs/charts.tsx b/docs/charts.tsx index 80a631bd42..838c1110a8 100644 --- a/docs/charts.tsx +++ b/docs/charts.tsx @@ -14,7 +14,7 @@ import { } from '../src'; import React from 'react'; import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { arrayKnobs, getChartRotationKnob } from '../stories/common'; +import { arrayKnobs, getChartRotationKnob } from '../stories/utils/knobs'; import { Icon } from '../src/components/icons/icon'; export default { @@ -39,7 +39,7 @@ export const Basic = () => { { const specId = toggleSpec ? 'areas1' : 'areas2'; return ( - + { const axisPosition = isBottom ? Position.Bottom : Position.Top; return ( - + { style={style} marker={} /> - - + + { const dataValues = generateAnnotationData(arrayKnobs('annotation values', ['a', 'c'])); return ( - + } /> - - - + + + 0uQ#P`J9Dws%A0<*x?e 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 WI=0n{<0pgzFu05I7t+pMz4KqxMHG+# literal 0 HcmV?d00001 diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-curvedwaxisandlegend-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-curved-with-axis-and-legend-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-curvedwaxisandlegend-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-curved-with-axis-and-legend-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-linear-w-axis-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-linear-with-axis-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-linear-w-axis-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-linear-with-axis-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiserieswithlogvalues-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multi-series-with-log-values-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiserieswithlogvalues-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multi-series-with-log-values-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiplewaxisandlegend-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiple-with-axis-and-legend-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiplewaxisandlegend-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-multiple-with-axis-and-legend-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-ordinal-w-axis-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-ordinal-with-axis-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-ordinal-w-axis-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-ordinal-with-axis-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-stackedwaxisandlegend-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-stacked-with-axis-and-legend-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-stackedwaxisandlegend-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-stacked-with-axis-and-legend-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-w-axis-and-legend-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-with-axis-and-legend-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-w-axis-and-legend-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-with-axis-and-legend-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-w-axis-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-with-axis-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-w-axis-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-line-chart-with-axis-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-remove-duplicate-scales-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-remove-duplicate-axis-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-remove-duplicate-scales-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-remove-duplicate-axis-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-year-scale-custom-timezone-same-tone-tooltip-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-specified-timezone-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-year-scale-custom-timezone-same-tone-tooltip-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-specified-timezone-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-line-chart-with-different-timezones-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-timezone-configuration-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-line-chart-with-different-timezones-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-timezone-configuration-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-utc-timezone-local-tooltip-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-tooltip-in-local-timezone-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-utc-timezone-local-tooltip-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-tooltip-in-local-timezone-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-utc-timezone-utc-tooltip-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-tooltip-in-utc-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-x-scale-utc-timezone-utc-tooltip-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-scales-tooltip-in-utc-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-chart-size-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-chart-size-visually-looks-correct-1-snap.png index 1ca8a8a4d57ea523136d7a2db80cceb9ce607fa1..6d4df86b469ce31071df36f0bf5b2cdf3f57559e 100644 GIT binary patch delta 1120 zcmeC@Y~q|?Sa0L$;uumf=j|;2oZBHX$M=V5vpaAoIyn0%w3%2Klsa85b9iiI@^6lX zGT)zzdd?hb8)MZ%3mg;_r`9_gtk7_cx}Mow79H;8zG-Rhu4TSgbu%yN`fmP{{cm}h z%<-}$^@j~2R=Tl|Ew;s9_$~8x&-3+vHs1fe_xYXRwPC9lylASQe)7Rqd-lSF+hu?L zTzbvB_2mUiVOP}Ic1ch014eEmI6X5X*vGbN^{EaNp1~I3N1e<#124YsoqCGv6R(KRY_7;>5=U6~sV@AIV)5ee^y}#pgt&Fznjd-oVOgq# ztzP@GI=c=1kAE85C+Y|t_&0I#i?Um<{=Hl0#3a)_-Q91|ZOet4JvW!AFxu*|*Pm;j z7P?*cgwdOm8q;p%cyH2}%JoO4c}KwigP9d4lDdS1bnAZgC@FLONGs$M58?P|B@Wm{ny{7_V~^%DhUVPA2jB-yV5m7ckxQ|BPX_=6q?;?E18>hF)fz&Vfg&j z?=DOf`>)%2p7rYVLzO#OPGB90|vL=~HrrLd4S=xbN7PxKO-gcP$J64oTM`Xex{<(T>7s6Ng=i56pS+%_^dRP`5i)B^BP(f@_A?$ubpur zDGKdZdKEmGBnG4N;48{ypM|(QHV=G?JrgIdIc~ZvVt@C@ulU&SB~j)|Oi2XSpw4zF zsuf7OX5XMr8eUaUF$_y`{deN<)gw<4jn^?_NdNQ*9zOmJq5|1zbhO0d>ES`VKRem< zx@5V8*C&VZ#r#~oO*V9RmXlcETVHf{Ob;;&Mb{B1WN}B&O--*8)Cx`{mY`Lu^|o|b z#2cPr?0?#{rR97b(?dE1CImpBki%#y4pGh-6ZKm}QK~r&LF@Vuj+^iIqb2#Y3i=$3tq%x6s$D#{3 zFy}!*S$#T~CL}LQc=y~m2A2LZD@9v;9}Z%_cYh4&Hg*Gog&fAxaY!bPHFY5=NyKyR zpYg#TXH82+1LNrgepm`NTxAz3G7bj37>b8&sBDG05JlHy>~agzMUD>JHBz8$t19U$Ig1&hks7PmrLQ*Z4cs=iQjCUp$)~uhzJ}W zy1kc3yfyt*gM}Q%(+P;G3a+9+G+}FgSuP`1Dnc_>wx_qSDBuOJ zkXc7H6!O;DQ@H=9ue(F?|&3m(6-`OMI2nhMFuGWw@aUv^`%iiGPO*O^+F*;K15`(|wt z;d5bY<(eN^-Ml+%1#D2K3?_s!m=MZfLMVd?p$sO3GMEs`U_vN^384%ogff^A%3wk$ zg9)JwCWJDW5XxXeD1!;13?_s!m=MZfLMY>3YaQ@|ddFb)00000NkvXXu0mjfy0;jT diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-naming-config-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-config-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-naming-config-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-config-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-series-name-formatting-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-formatting-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-series-name-formatting-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-formatting-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-series-name-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-series-name-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-name-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-theme-styling-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-theme-styling-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..42d2f06ab1b9aabb3bac23add055e9bc139a7475 GIT binary patch literal 53539 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 literal 0 HcmV?d00001 diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-full-zero-slice-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-full-and-zero-slices-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-full-zero-slice-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-full-and-zero-slices-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slice-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slices-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slice-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-high-number-of-slices-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-large-small-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-large-and-small-slices-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-large-small-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-large-and-small-slices-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-simple-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-simple-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-most-basic-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-big-empty-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-near-full-near-empty-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-big-empty-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-near-full-near-empty-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-no-pie-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-no-pie-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-negative-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-no-slice-no-pie-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-no-slice-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-no-slice-no-pie-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-no-slice-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-slice-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-slice-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-slice-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-slice-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-small-slice-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-small-sice-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-small-slice-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-small-sice-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-very-small-slice-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-very-small-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-very-small-slice-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-single-very-small-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-three-layers-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-three-layers-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-three-layers-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-two-layers-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-two-layers-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-sunburst-with-two-layers-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-total-zero-no-pie-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-total-zero-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-total-zero-no-pie-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-total-zero-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-pie-chart-2-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-pie-chart-2-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-value-formatted-with-categorical-color-palette-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-very-large-small-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-very-large-and-small-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-very-large-small-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-very-large-and-small-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-pie-chart-labels-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-direct-text-labels-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-pie-chart-labels-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-direct-text-labels-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-pie-chart-with-fill-labels-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-pie-chart-with-fill-labels-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-fill-labels-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-two-slices-pie-chart-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-two-slices-visually-looks-correct-1-snap.png similarity index 100% rename from integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-two-slices-pie-chart-visually-looks-correct-1-snap.png rename to integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-sunburst-with-two-slices-visually-looks-correct-1-snap.png diff --git a/integration/tests/__image_snapshots__/axis-stories-test-ts-axis-stories-should-render-with-domain-constraints-1-snap.png b/integration/tests/__image_snapshots__/axis-stories-test-ts-axis-stories-should-render-with-domain-constraints-1-snap.png index 8848e6cb37892de1058ad227724cee2c94f16824..1b2c4339f97d4a881818db68420adcd39434f31a 100644 GIT binary patch literal 26134 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 { describe('rotation', () => { it('rotation - 0', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/annotations--test-line-annotation-single-value-histogram&knob-debug=&knob-chartRotation=0', + 'http://localhost:9001/?path=/story/annotations-lines--single-bar-histogram&knob-debug=&knob-chartRotation=0', ); }); it('rotation - 90', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/annotations--test-line-annotation-single-value-histogram&knob-debug=&knob-chartRotation=90', + 'http://localhost:9001/?path=/story/annotations-lines--single-bar-histogram&knob-debug=&knob-chartRotation=90', ); }); it('rotation - negative 90', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/annotations--test-line-annotation-single-value-histogram&knob-debug=&knob-chartRotation=-90', + 'http://localhost:9001/?path=/story/annotations-lines--single-bar-histogram&knob-debug=&knob-chartRotation=-90', ); }); it('rotation - 180', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/annotations--test-line-annotation-single-value-histogram&knob-debug=&knob-chartRotation=180', + 'http://localhost:9001/?path=/story/annotations-lines--single-bar-histogram&knob-debug=&knob-chartRotation=180', ); }); }); diff --git a/integration/tests/area_stories.test.ts b/integration/tests/area_stories.test.ts index ff4c5fdf01..833eae5b98 100644 --- a/integration/tests/area_stories.test.ts +++ b/integration/tests/area_stories.test.ts @@ -3,13 +3,13 @@ import { common } from '../page_objects'; describe('Area series stories', () => { it('stacked as NOT percentage', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/area-chart--stacked-as-percentage&knob-stacked as percentage=', + 'http://localhost:9001/?path=/story/area-chart--stacked-percentage&knob-stacked as percentage=', ); }); describe('accessorFormats', () => { it('should show custom format', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/area-chart--band-area-chart&knob-scale to extent=&knob-y0AccessorFormat= [min]&knob-y1AccessorFormat= [max]', + 'http://localhost:9001/?path=/story/area-chart--band-area&knob-scale to extent=&knob-y0AccessorFormat= [min]&knob-y1AccessorFormat= [max]', ); }); }); @@ -17,24 +17,24 @@ describe('Area series stories', () => { describe('scaleyScaleToDataExtent is true', () => { it('should show correct extents - Banded', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/area-chart--stacked-band-area-chart&knob-scale to extent=true', + 'http://localhost:9001/?path=/story/area-chart--stacked-band&knob-scale to extent=true', ); }); it('should show correct extents - stacked', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/area-chart--stacked-band-area-chart&knob-scale to extent=true', + 'http://localhost:9001/?path=/story/area-chart--stacked-band&knob-scale to extent=true', ); }); }); describe('scaleyScaleToDataExtent is false', () => { it('should show correct extents - Banded', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/area-chart--stacked-band-area-chart&knob-scale to extent=false', + 'http://localhost:9001/?path=/story/area-chart--stacked-band&knob-scale to extent=false', ); }); it('should show correct extents - stacked', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/area-chart--stacked-band-area-chart&knob-scale to extent=false', + 'http://localhost:9001/?path=/story/area-chart--stacked-band&knob-scale to extent=false', ); }); }); diff --git a/integration/tests/axis_stories.test.ts b/integration/tests/axis_stories.test.ts index 60491891c9..1da8ac88cd 100644 --- a/integration/tests/axis_stories.test.ts +++ b/integration/tests/axis_stories.test.ts @@ -3,47 +3,47 @@ import { common } from '../page_objects'; describe('Axis stories', () => { it('should render proper tick count', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--basic&knob-Tick Label Padding=0&knob-debug=&knob-Bottom overlap labels=&knob-Bottom overlap ticks=true&knob-Number of ticks on bottom=20&knob-Left overlap labels=&knob-Left overlap ticks=true&knob-Number of ticks on left=10', + 'http://localhost:9001/?path=/story/axes--basic&knob-Tick Label Padding=0&knob-debug=&knob-Bottom overlap labels=&knob-Bottom overlap ticks=true&knob-Number of ticks on bottom=20&knob-Left overlap labels=&knob-Left overlap ticks=true&knob-Number of ticks on left=10', ); }); it('should render proper tick count with showOverlappingLabels', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--basic&knob-Tick Label Padding=0&knob-debug=&knob-Bottom overlap labels_Bottom Axis=true&knob-Bottom overlap ticks_Bottom Axis=true&knob-Number of ticks on bottom_Bottom Axis=20&knob-Left overlap labels_Left Axis=&knob-Left overlap ticks_Left Axis=true&knob-Number of ticks on left_Left Axis=10', + 'http://localhost:9001/?path=/story/axes--basic&knob-Tick Label Padding=0&knob-debug=&knob-Bottom overlap labels_Bottom Axis=true&knob-Bottom overlap ticks_Bottom Axis=true&knob-Number of ticks on bottom_Bottom Axis=20&knob-Left overlap labels_Left Axis=&knob-Left overlap ticks_Left Axis=true&knob-Number of ticks on left_Left Axis=10', ); }); it('should render ticks with varied rotations', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', + 'http://localhost:9001/?path=/story/axes--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', ); }); it('should hide bottom axis', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=true&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', + 'http://localhost:9001/?path=/story/axes--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=true&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', ); }); it('should hide top axis', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=true&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', + 'http://localhost:9001/?path=/story/axes--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=true&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', ); }); it('should hide left axis', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=true&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', + 'http://localhost:9001/?path=/story/axes--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=true&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=&knob-debug=', ); }); it('should hide right axis', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=true&knob-debug=', + 'http://localhost:9001/?path=/story/axes--tick-label-rotation&knob-Tick Label Padding=0&knob-bottom axis tick label rotation=47&knob-hide bottom axis=&knob-left axis tick label rotation=-56&knob-hide left axis=&knob-top axis tick label rotation=-59&knob-hide top axis=&knob-right axis tick label rotation=30&knob-hide right axis=true&knob-debug=', ); }); it('should render tick padding', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--w-many-tick-labels&knob-Tick Label Padding=60', + 'http://localhost:9001/?path=/story/axes--many-tick-labels&knob-Tick Label Padding=60', ); }); it('should render with domain constraints', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/axis--customizing-domain-limits-only-one-bound-defined&knob-left min=2&knob-xDomain max=2', + 'http://localhost:9001/?path=/story/axes--custom-mixed&knob-left min=2&knob-xDomain max=2', ); }); }); diff --git a/integration/tests/line_stories.test.ts b/integration/tests/line_stories.test.ts index b03b773181..dffc0a3316 100644 --- a/integration/tests/line_stories.test.ts +++ b/integration/tests/line_stories.test.ts @@ -4,22 +4,22 @@ describe('Line series stories', () => { describe('rotation', () => { it('rotation - 0', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/line-chart--ordinal-w-axis&knob-chartRotation=0', + 'http://localhost:9001/?path=/story/line-chart--ordinal-with-axis&knob-chartRotation=0', ); }); it('rotation - 90', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/line-chart--ordinal-w-axis&knob-chartRotation=90', + 'http://localhost:9001/?path=/story/line-chart--ordinal-with-axis&knob-chartRotation=90', ); }); it('rotation - negative 90', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/line-chart--ordinal-w-axis&knob-chartRotation=-90', + 'http://localhost:9001/?path=/story/line-chart--ordinal-with-axis&knob-chartRotation=-90', ); }); it('rotation - 180', async () => { await common.expectChartAtUrlToMatchScreenshot( - 'http://localhost:9001/?path=/story/line-chart--ordinal-w-axis&knob-chartRotation=180', + 'http://localhost:9001/?path=/story/line-chart--ordinal-with-axis&knob-chartRotation=180', ); }); }); diff --git a/package.json b/package.json index 7648e9efb8..0d035eb95c 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "@storybook/addon-info": "^5.2.8", "@storybook/addon-knobs": "5.2.8", "@storybook/addon-links": "^5.3.9", + "@storybook/addon-storysource": "^5.3.13", "@storybook/preset-typescript": "^1.1.0", "@storybook/react": "5.2.8", "@storybook/source-loader": "^5.3.9", diff --git a/src/components/chart.test.tsx b/src/components/chart.test.tsx index 8ef0f9ab2e..2cf1d7a706 100644 --- a/src/components/chart.test.tsx +++ b/src/components/chart.test.tsx @@ -19,7 +19,7 @@ describe('Chart', () => { it("should render 'No data to display' with settings but without series", () => { const wrapper = render( - + , ); expect(wrapper.text()).toContain('No data to display'); diff --git a/src/components/chart_status.tsx b/src/components/chart_status.tsx index 14624a3421..66c6acb54c 100644 --- a/src/components/chart_status.tsx +++ b/src/components/chart_status.tsx @@ -19,7 +19,9 @@ class ChartStatusComponent extends React.Component { dispatchRenderChange = () => { const { onRenderChange, rendered } = this.props; if (onRenderChange) { - onRenderChange(rendered); + window.requestAnimationFrame(() => { + onRenderChange(rendered); + }); } }; render() { diff --git a/src/components/legend/legend.test.tsx b/src/components/legend/legend.test.tsx index 593d18479d..f64b6a1781 100644 --- a/src/components/legend/legend.test.tsx +++ b/src/components/legend/legend.test.tsx @@ -83,7 +83,7 @@ describe('Legend', () => { id="areas" xScaleType={ScaleType.Linear} yScaleType={ScaleType.Linear} - xAccessor={'x'} + xAccessor="x" yAccessors={['y']} splitSeriesAccessors={['g']} data={data} @@ -113,7 +113,7 @@ describe('Legend', () => { id="areas" xScaleType={ScaleType.Linear} yScaleType={ScaleType.Linear} - xAccessor={'x'} + xAccessor="x" yAccessors={['y']} splitSeriesAccessors={['g']} data={data} diff --git a/src/specs/specs_parser.test.tsx b/src/specs/specs_parser.test.tsx index 65489d2f41..5b13378a43 100644 --- a/src/specs/specs_parser.test.tsx +++ b/src/specs/specs_parser.test.tsx @@ -31,7 +31,7 @@ describe('Specs parser', () => { { ]} /> { ]} /> { { children: ( ({ dataValue: value, details: `detail-${index}` })); -} - -function generateTimeAnnotationData(values: any[]): LineAnnotationDatum[] { - return values.map((value, index) => ({ - dataValue: value, - details: `detail-${index}`, - header: dateFormatter(value), - })); -} - -export default { - title: 'Annotations', - parameters: { - info: { - source: false, - }, - }, -}; - -export const lineBasicXDomainContinous = () => { - const data = arrayKnobs('data values', [2.5, 7.2]); - const dataValues = generateAnnotationData(data); - - const style = { - line: { - strokeWidth: 3, - stroke: '#f00', - opacity: 1, - }, - details: { - fontSize: 12, - fontFamily: 'Arial', - fontStyle: 'bold', - fill: 'gray', - padding: 0, - }, - }; - - const isBottom = boolean('x domain axis is bottom', true); - const axisPosition = isBottom ? Position.Bottom : Position.Top; - - return ( - - - } - /> - - - - - ); -}; -lineBasicXDomainContinous.story = { - name: '[line] basic xDomain continuous', -}; - -export const lineBasicXDomainOrdinal = () => { - const dataValues = generateAnnotationData(arrayKnobs('annotation values', ['a', 'c'])); - return ( - - - } - /> - - - - - - ); -}; -lineBasicXDomainOrdinal.story = { - name: '[line] basic xDomain ordinal', -}; - -export const lineBasicYDomain = () => { - const data = arrayKnobs('data values', [1.5, 7.2]); - const dataValues = generateAnnotationData(data); - - const isLeft = boolean('y-domain axis is Position.Left', true); - const axisTitle = isLeft ? 'y-domain axis (left)' : 'y-domain axis (right)'; - const axisPosition = isLeft ? Position.Left : Position.Right; - - return ( - - - } - /> - - - - - ); -}; -lineBasicYDomain.story = { - name: '[line] basic yDomain', -}; - -export const lineTimeSeries = () => { - const dataValues = generateTimeAnnotationData([ - 1551438150000, - 1551438180000, - 1551438390000, - 1551438450000, - 1551438480000, - ]); - - return ( - - - } - /> - - - - - ); -}; -lineTimeSeries.story = { - name: '[line] time series', -}; - -export const lineStyling = () => { - const data = [2.5, 7.2]; - const dataValues = generateAnnotationData(data); - - const dashWidth = number('dash line width', 1); - const dashGapWidth = number('dash gap width', 0); - - const style = { - line: { - strokeWidth: number('line stroke width', 3), - stroke: color('line & marker color', '#f00'), - dash: [dashWidth, dashGapWidth], - opacity: number('line opacity', 1, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - }, - }; - - const axisPosition = Position.Bottom; - - const marker = select<'alert' | 'eye' | 'questionInCircle'>( - 'marker icon (examples from internal Icon library)', - { - alert: 'alert', - eye: 'eye', - questionInCircle: 'questionInCircle', - }, - 'alert', - ); - - const hideLines = boolean('annotation lines hidden', false); - const hideTooltips = boolean('annotation tooltips hidden', false); - - return ( - - - } - hideLines={hideLines} - hideTooltips={hideTooltips} - /> - - - - - ); -}; -lineStyling.story = { - name: '[line] styling', -}; - -export const rectBasicAnnotationLinearBar = () => { - const dataValues = [ - { - coordinates: { - x0: 0, - x1: 1, - y0: 0, - y1: 7, - }, - details: 'details about this annotation', - }, - ]; - - return ( - - - - - - - - ); -}; -rectBasicAnnotationLinearBar.story = { - name: '[rect] basic annotation (linear bar)', -}; - -export const rectBasicAnnotationOrdinalBar = () => { - const dataValues = [ - { - coordinates: { - x0: 'a', - x1: 'b', - }, - details: 'details about this annotation', - }, - ]; - - return ( - - - - - - - - ); -}; -rectBasicAnnotationOrdinalBar.story = { - name: '[rect] basic annotation (ordinal bar)', -}; - -export const rectBasicAnnotationLine = () => { - const definedCoordinate = select( - 'defined coordinate', - { - x0: 'x0', - x1: 'x1', - y0: BandedAccessorType.Y0, - y1: BandedAccessorType.Y1, - }, - 'x0', - ); - - const dataValues = [ - { - coordinates: { - x0: 1, - x1: 1.25, - y0: 0, - y1: 7, - }, - details: 'details about this annotation', - }, - { - coordinates: { - x0: 2.0, - x1: 2.1, - y0: 0, - y1: 7, - }, - details: 'details about this annotation', - }, - { - coordinates: { - x0: definedCoordinate === 'x0' ? 0.25 : null, - x1: definedCoordinate === 'x1' ? 2.75 : null, - y0: definedCoordinate === BandedAccessorType.Y0 ? 0.25 : null, - y1: definedCoordinate === BandedAccessorType.Y1 ? 6.75 : null, - }, - details: 'can have null values', - }, - ]; - - const isLeft = boolean('y-domain axis is Position.Left', true); - const yAxisTitle = isLeft ? 'y-domain axis (left)' : 'y-domain axis (right)'; - const yAxisPosition = isLeft ? Position.Left : Position.Right; - - const isBottom = boolean('x-domain axis is Position.Bottom', true); - const xAxisTitle = isBottom ? 'x-domain axis (botttom)' : 'x-domain axis (top)'; - const xAxisPosition = isBottom ? Position.Bottom : Position.Top; - - return ( - - - - - - - - ); -}; -rectBasicAnnotationLine.story = { - name: '[rect] basic annotation (line)', -}; - -export const rectStyling = () => { - const dataValues = [ - { - coordinates: { - x0: 0, - x1: 0.25, - y0: 0, - y1: 7, - }, - details: 'annotation 1', - }, - { - coordinates: { - x0: -0.1, - x1: 0, - y0: 0, - y1: 7, - }, - details: 'annotation 2', - }, - { - coordinates: { - x0: 1.1, - x1: 1.3, - y0: 0, - y1: 7, - }, - details: 'annotation 2', - }, - { - coordinates: { - x0: 2.5, - x1: 3, - y0: 0, - y1: 7, - }, - details: 'annotation 3', - }, - ]; - - const zIndex = number('annotation zIndex', 0); - - const style = { - strokeWidth: number('rect border stroke width', 1), - stroke: color('rect border stroke color', '#e5e5e5'), - fill: color('fill color', '#e5e5e5'), - opacity: number('annotation opacity', 0.5, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - }; - - const hasCustomTooltip = boolean('has custom tooltip render', false); - - const customTooltip = (details?: string) => ( -
- - {details} -
- ); - const renderTooltip = hasCustomTooltip ? customTooltip : undefined; - - const isLeft = boolean('y-domain axis is Position.Left', true); - const yAxisTitle = isLeft ? 'y-domain axis (left)' : 'y-domain axis (right)'; - const yAxisPosition = isLeft ? Position.Left : Position.Right; - - const isBottom = boolean('x-domain axis is Position.Bottom', true); - const xAxisTitle = isBottom ? 'x-domain axis (botttom)' : 'x-domain axis (top)'; - const xAxisPosition = isBottom ? Position.Bottom : Position.Top; - - return ( - - - - - - - - ); -}; -rectStyling.story = { - name: '[rect] styling', -}; - -export const testLineAnnotationSingleValueHistogram = () => { - const dataValues = [ - { - dataValue: 3.5, - }, - ]; - - const style = { - line: { - strokeWidth: 3, - stroke: '#f00', - opacity: 1, - }, - details: { - fontSize: 12, - fontFamily: 'Arial', - fontStyle: 'bold', - fill: 'gray', - padding: 0, - }, - }; - - const xDomain = { - minInterval: 1, - }; - - return ( - - - - - - - - ); -}; -testLineAnnotationSingleValueHistogram.story = { - name: '[test] line annotation single value histogram', -}; - -export const rectTooltipVisilibityDependentOnContent = () => { - const tooltipOptions = { - 'default formatter, details defined': 'default_defined', - 'default formatter, details undefined': 'default_undefined', - 'custom formatter, return element': 'custom_element', - 'custom formatter, return null': 'custom_null', - }; - - const tooltipFormat = select('tooltip format', tooltipOptions, 'default_defined'); - - const isDefaultDefined = tooltipFormat === 'default_defined'; - - const dataValues = [ - { - coordinates: { - x0: 0, - x1: 1, - y0: 0, - y1: 7, - }, - details: isDefaultDefined ? 'foo' : undefined, - }, - ]; - - const isCustomTooltipElement = tooltipFormat === 'custom_element'; - const tooltipFormatter: AnnotationTooltipFormatter = () => { - if (!isCustomTooltipElement) { - return null; - } - - return
{'custom formatter'}
; - }; - - const isCustomTooltip = tooltipFormat.includes('custom'); - - return ( - - - - - - - ); -}; -rectTooltipVisilibityDependentOnContent.story = { - name: '[rect] tooltip visibility dependent on content', -}; diff --git a/stories/annotations/lines/1_x_continuous.tsx b/stories/annotations/lines/1_x_continuous.tsx new file mode 100644 index 0000000000..88bad1a91c --- /dev/null +++ b/stories/annotations/lines/1_x_continuous.tsx @@ -0,0 +1,71 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { + AnnotationDomainTypes, + Axis, + BarSeries, + Chart, + LineAnnotation, + LineAnnotationDatum, + ScaleType, + Settings, +} from '../../../src'; +import { Icon } from '../../../src/components/icons/icon'; +import { getChartRotationKnob, arrayKnobs } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +function generateAnnotationData(values: any[]): LineAnnotationDatum[] { + return values.map((value, index) => ({ dataValue: value, details: `detail-${index}` })); +} + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + const data = arrayKnobs('data values', [2.5, 7.2]); + const dataValues = generateAnnotationData(data); + + const style = { + line: { + strokeWidth: 3, + stroke: '#f00', + opacity: 1, + }, + details: { + fontSize: 12, + fontFamily: 'Arial', + fontStyle: 'bold', + fill: 'gray', + padding: 0, + }, + }; + + const isBottom = boolean('x domain axis is bottom', true); + const axisPosition = isBottom ? Position.Bottom : Position.Top; + + return ( + + + } + /> + + + + + ); +}; diff --git a/stories/annotations/lines/2_x_ordinal.tsx b/stories/annotations/lines/2_x_ordinal.tsx new file mode 100644 index 0000000000..2a781c6c76 --- /dev/null +++ b/stories/annotations/lines/2_x_ordinal.tsx @@ -0,0 +1,52 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { + AnnotationDomainTypes, + Axis, + BarSeries, + Chart, + LineAnnotation, + LineAnnotationDatum, + ScaleType, + Settings, +} from '../../../src'; +import { Icon } from '../../../src/components/icons/icon'; +import { getChartRotationKnob, arrayKnobs } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +function generateAnnotationData(values: any[]): LineAnnotationDatum[] { + return values.map((value, index) => ({ dataValue: value, details: `detail-${index}` })); +} + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + const dataValues = generateAnnotationData(arrayKnobs('annotation values', ['a', 'c'])); + return ( + + + } + /> + + + + + + ); +}; diff --git a/stories/annotations/lines/3_x_time.tsx b/stories/annotations/lines/3_x_time.tsx new file mode 100644 index 0000000000..9a75233543 --- /dev/null +++ b/stories/annotations/lines/3_x_time.tsx @@ -0,0 +1,62 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { + AnnotationDomainTypes, + Axis, + BarSeries, + Chart, + LineAnnotation, + LineAnnotationDatum, + ScaleType, + Settings, + timeFormatter, +} from '../../../src'; +import { Icon } from '../../../src/components/icons/icon'; +import { KIBANA_METRICS } from '../../../src/utils/data_samples/test_dataset_kibana'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +const dateFormatter = timeFormatter('HH:mm:ss'); + +function generateTimeAnnotationData(values: any[]): LineAnnotationDatum[] { + return values.map((value, index) => ({ + dataValue: value, + details: `detail-${index}`, + header: dateFormatter(value), + })); +} + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const dataValues = generateTimeAnnotationData([ + 1551438150000, + 1551438180000, + 1551438390000, + 1551438450000, + 1551438480000, + ]); + + return ( + + + } + /> + + + + + ); +}; diff --git a/stories/annotations/lines/4_y_domain.tsx b/stories/annotations/lines/4_y_domain.tsx new file mode 100644 index 0000000000..c297555a71 --- /dev/null +++ b/stories/annotations/lines/4_y_domain.tsx @@ -0,0 +1,58 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { + AnnotationDomainTypes, + Axis, + BarSeries, + Chart, + LineAnnotation, + LineAnnotationDatum, + ScaleType, + Settings, +} from '../../../src'; +import { Icon } from '../../../src/components/icons/icon'; +import { getChartRotationKnob, arrayKnobs } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +function generateAnnotationData(values: any[]): LineAnnotationDatum[] { + return values.map((value, index) => ({ dataValue: value, details: `detail-${index}` })); +} + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const data = arrayKnobs('data values', [1.5, 7.2]); + const dataValues = generateAnnotationData(data); + + const isLeft = boolean('y-domain axis is Position.Left', true); + const axisTitle = isLeft ? 'y-domain axis (left)' : 'y-domain axis (right)'; + const axisPosition = isLeft ? Position.Left : Position.Right; + + return ( + + + } + /> + + + + + ); +}; diff --git a/stories/annotations/lines/5_styling.tsx b/stories/annotations/lines/5_styling.tsx new file mode 100644 index 0000000000..f395ded245 --- /dev/null +++ b/stories/annotations/lines/5_styling.tsx @@ -0,0 +1,88 @@ +import { boolean, color, number, select } from '@storybook/addon-knobs'; +import React from 'react'; +import { + AnnotationDomainTypes, + Axis, + BarSeries, + Chart, + LineAnnotation, + LineAnnotationDatum, + ScaleType, + Settings, +} from '../../../src'; +import { Icon } from '../../../src/components/icons/icon'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +function generateAnnotationData(values: any[]): LineAnnotationDatum[] { + return values.map((value, index) => ({ dataValue: value, details: `detail-${index}` })); +} + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const data = [2.5, 7.2]; + const dataValues = generateAnnotationData(data); + + const dashWidth = number('dash line width', 1); + const dashGapWidth = number('dash gap width', 0); + + const style = { + line: { + strokeWidth: number('line stroke width', 3), + stroke: color('line & marker color', '#f00'), + dash: [dashWidth, dashGapWidth], + opacity: number('line opacity', 1, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + }, + }; + + const axisPosition = Position.Bottom; + + const marker = select<'alert' | 'eye' | 'questionInCircle'>( + 'marker icon (examples from internal Icon library)', + { + alert: 'alert', + eye: 'eye', + questionInCircle: 'questionInCircle', + }, + 'alert', + ); + + const hideLines = boolean('annotation lines hidden', false); + const hideTooltips = boolean('annotation tooltips hidden', false); + + return ( + + + } + hideLines={hideLines} + hideTooltips={hideTooltips} + /> + + + + + ); +}; diff --git a/stories/annotations/lines/6_test_single_bar_histogram.tsx b/stories/annotations/lines/6_test_single_bar_histogram.tsx new file mode 100644 index 0000000000..34543cacc4 --- /dev/null +++ b/stories/annotations/lines/6_test_single_bar_histogram.tsx @@ -0,0 +1,58 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { AnnotationDomainTypes, Axis, BarSeries, Chart, LineAnnotation, ScaleType, Settings } from '../../../src'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const dataValues = [ + { + dataValue: 3.5, + }, + ]; + + const style = { + line: { + strokeWidth: 3, + stroke: '#f00', + opacity: 1, + }, + details: { + fontSize: 12, + fontFamily: 'Arial', + fontStyle: 'bold', + fill: 'gray', + padding: 0, + }, + }; + + const xDomain = { + minInterval: 1, + }; + + return ( + + + + + + + + ); +}; diff --git a/stories/annotations/lines/line.stories.tsx b/stories/annotations/lines/line.stories.tsx new file mode 100644 index 0000000000..948b4f816a --- /dev/null +++ b/stories/annotations/lines/line.stories.tsx @@ -0,0 +1,16 @@ +import { SB_KNOBS_PANEL } from '../../utils/storybook'; + +export default { + title: 'Annotations/Lines', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as xContinuousDomain } from './1_x_continuous'; +export { example as xOrdinalDomain } from './2_x_ordinal'; +export { example as xTimeDomain } from './3_x_time'; +export { example as yDomain } from './4_y_domain'; +export { example as styling } from './5_styling'; +// for testing +export { example as singleBarHistogram } from './6_test_single_bar_histogram'; diff --git a/stories/annotations/rects/1_linear_bar_chart.tsx b/stories/annotations/rects/1_linear_bar_chart.tsx new file mode 100644 index 0000000000..a757ea4ebb --- /dev/null +++ b/stories/annotations/rects/1_linear_bar_chart.tsx @@ -0,0 +1,43 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, RectAnnotation, ScaleType, Settings } from '../../../src'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const dataValues = [ + { + coordinates: { + x0: 0, + x1: 1, + y0: 0, + y1: 7, + }, + details: 'details about this annotation', + }, + ]; + + return ( + + + + + + + + ); +}; diff --git a/stories/annotations/rects/2_ordinal_bar_chart.tsx b/stories/annotations/rects/2_ordinal_bar_chart.tsx new file mode 100644 index 0000000000..c246fe2a1b --- /dev/null +++ b/stories/annotations/rects/2_ordinal_bar_chart.tsx @@ -0,0 +1,42 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, RectAnnotation, ScaleType, Settings } from '../../../src'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const dataValues = [ + { + coordinates: { + x0: 'a', + x1: 'b', + }, + details: 'details about this annotation', + }, + ]; + + return ( + + + + + + + + ); +}; diff --git a/stories/annotations/rects/3_linear_line_chart.tsx b/stories/annotations/rects/3_linear_line_chart.tsx new file mode 100644 index 0000000000..2e238526e1 --- /dev/null +++ b/stories/annotations/rects/3_linear_line_chart.tsx @@ -0,0 +1,81 @@ +import { boolean, select } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, Chart, LineSeries, RectAnnotation, ScaleType, Settings } from '../../../src'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { BandedAccessorType } from '../../../src/utils/geometry'; +import { Position } from '../../../src/utils/commons'; + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const definedCoordinate = select( + 'defined coordinate', + { + x0: 'x0', + x1: 'x1', + y0: BandedAccessorType.Y0, + y1: BandedAccessorType.Y1, + }, + 'x0', + ); + + const dataValues = [ + { + coordinates: { + x0: 1, + x1: 1.25, + y0: 0, + y1: 7, + }, + details: 'details about this annotation', + }, + { + coordinates: { + x0: 2.0, + x1: 2.1, + y0: 0, + y1: 7, + }, + details: 'details about this annotation', + }, + { + coordinates: { + x0: definedCoordinate === 'x0' ? 0.25 : null, + x1: definedCoordinate === 'x1' ? 2.75 : null, + y0: definedCoordinate === BandedAccessorType.Y0 ? 0.25 : null, + y1: definedCoordinate === BandedAccessorType.Y1 ? 6.75 : null, + }, + details: 'can have null values', + }, + ]; + + const isLeft = boolean('y-domain axis is Position.Left', true); + const yAxisTitle = isLeft ? 'y-domain axis (left)' : 'y-domain axis (right)'; + const yAxisPosition = isLeft ? Position.Left : Position.Right; + + const isBottom = boolean('x-domain axis is Position.Bottom', true); + const xAxisTitle = isBottom ? 'x-domain axis (botttom)' : 'x-domain axis (top)'; + const xAxisPosition = isBottom ? Position.Bottom : Position.Top; + + return ( + + + + + + + + ); +}; diff --git a/stories/annotations/rects/4_styling.tsx b/stories/annotations/rects/4_styling.tsx new file mode 100644 index 0000000000..8ee938662e --- /dev/null +++ b/stories/annotations/rects/4_styling.tsx @@ -0,0 +1,111 @@ +import { boolean, color, number } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, Chart, LineSeries, RectAnnotation, ScaleType, Settings } from '../../../src'; +import { Icon } from '../../../src/components/icons/icon'; +import { getChartRotationKnob } from '../../utils/knobs'; +import { Position } from '../../../src/utils/commons'; + +export const example = () => { + const debug = boolean('debug', false); + const rotation = getChartRotationKnob(); + + const dataValues = [ + { + coordinates: { + x0: 0, + x1: 0.25, + y0: 0, + y1: 7, + }, + details: 'annotation 1', + }, + { + coordinates: { + x0: -0.1, + x1: 0, + y0: 0, + y1: 7, + }, + details: 'annotation 2', + }, + { + coordinates: { + x0: 1.1, + x1: 1.3, + y0: 0, + y1: 7, + }, + details: 'annotation 2', + }, + { + coordinates: { + x0: 2.5, + x1: 3, + y0: 0, + y1: 7, + }, + details: 'annotation 3', + }, + ]; + + const zIndex = number('annotation zIndex', 0); + + const style = { + strokeWidth: number('rect border stroke width', 1), + stroke: color('rect border stroke color', '#e5e5e5'), + fill: color('fill color', '#e5e5e5'), + opacity: number('annotation opacity', 0.5, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + }; + + const hasCustomTooltip = boolean('has custom tooltip render', false); + + const customTooltip = (details?: string) => ( +
+ + {details} +
+ ); + const renderTooltip = hasCustomTooltip ? customTooltip : undefined; + + const isLeft = boolean('y-domain axis is Position.Left', true); + const yAxisTitle = isLeft ? 'y-domain axis (left)' : 'y-domain axis (right)'; + const yAxisPosition = isLeft ? Position.Left : Position.Right; + + const isBottom = boolean('x-domain axis is Position.Bottom', true); + const xAxisTitle = isBottom ? 'x-domain axis (botttom)' : 'x-domain axis (top)'; + const xAxisPosition = isBottom ? Position.Bottom : Position.Top; + const hideTooltips = boolean('hide tooltips', false); + + return ( + + + + + + + + ); +}; diff --git a/stories/annotations/rects/5_tooltip_visibility.tsx b/stories/annotations/rects/5_tooltip_visibility.tsx new file mode 100644 index 0000000000..6b330b201f --- /dev/null +++ b/stories/annotations/rects/5_tooltip_visibility.tsx @@ -0,0 +1,64 @@ +import { select } from '@storybook/addon-knobs'; +import React from 'react'; +import { AnnotationTooltipFormatter, Axis, BarSeries, Chart, ScaleType, RectAnnotation } from '../../../src'; +import { Position } from '../../../src/utils/commons'; + +export const example = () => { + const tooltipOptions = { + 'default formatter, details defined': 'default_defined', + 'default formatter, details undefined': 'default_undefined', + 'custom formatter, return element': 'custom_element', + 'custom formatter, return null': 'custom_null', + }; + + const tooltipFormat = select('tooltip format', tooltipOptions, 'default_defined'); + + const isDefaultDefined = tooltipFormat === 'default_defined'; + + const dataValues = [ + { + coordinates: { + x0: 0, + x1: 1, + y0: 0, + y1: 7, + }, + details: isDefaultDefined ? 'foo' : undefined, + }, + ]; + + const isCustomTooltipElement = tooltipFormat === 'custom_element'; + const tooltipFormatter: AnnotationTooltipFormatter = () => { + if (!isCustomTooltipElement) { + return null; + } + + return
custom formatter
; + }; + + const isCustomTooltip = tooltipFormat.includes('custom'); + + return ( + + + + + + + ); +}; diff --git a/stories/annotations/rects/rects.stories.tsx b/stories/annotations/rects/rects.stories.tsx new file mode 100644 index 0000000000..6c55a43332 --- /dev/null +++ b/stories/annotations/rects/rects.stories.tsx @@ -0,0 +1,14 @@ +import { SB_KNOBS_PANEL } from '../../utils/storybook'; + +export default { + title: 'Annotations/Rects', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as linearBarChart } from './1_linear_bar_chart'; +export { example as ordinalBarChart } from './2_ordinal_bar_chart'; +export { example as linearLineChart } from './3_linear_line_chart'; +export { example as styling } from './4_styling'; +export { example as tooltipVisibility } from './5_tooltip_visibility'; diff --git a/stories/area/10_stacked_same_naming.tsx b/stories/area/10_stacked_same_naming.tsx new file mode 100644 index 0000000000..e4f07dfd50 --- /dev/null +++ b/stories/area/10_stacked_same_naming.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} + /> + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/11_test_linear.tsx b/stories/area/11_test_linear.tsx new file mode 100644 index 0000000000..4a069690d9 --- /dev/null +++ b/stories/area/11_test_linear.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 4], + [7, 3], + [8, 2], + [9, 1], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/12_test_time.tsx b/stories/area/12_test_time.tsx new file mode 100644 index 0000000000..90e1d967c1 --- /dev/null +++ b/stories/area/12_test_time.tsx @@ -0,0 +1,49 @@ +import { DateTime } from 'luxon'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +// for testing purposes only +export const example = () => { + const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); + const data = [ + [start.toMillis(), 1], + [start.plus({ minute: 1 }).toMillis(), 2], + [start.plus({ minute: 2 }).toMillis(), 3], + [start.plus({ minute: 3 }).toMillis(), 4], + [start.plus({ minute: 4 }).toMillis(), 5], + [start.plus({ minute: 5 }).toMillis(), 4], + [start.plus({ minute: 6 }).toMillis(), 3], + [start.plus({ minute: 7 }).toMillis(), 2], + [start.plus({ minute: 8 }).toMillis(), 1], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/13_band_area.tsx b/stories/area/13_band_area.tsx new file mode 100644 index 0000000000..27bd089e66 --- /dev/null +++ b/stories/area/13_band_area.tsx @@ -0,0 +1,77 @@ +import { boolean, text } from '@storybook/addon-knobs'; +import React from 'react'; +import { + AreaSeries, + Axis, + Chart, + CurveType, + LineSeries, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { getRandomNumberGenerator } from '../../src/mocks/utils'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + const getRandomNumber = getRandomNumberGenerator(); + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { + return { + x: d[0], + max: d[1] + 4 + 4 * getRandomNumber(), + min: d[1] - 4 - 4 * getRandomNumber(), + }; + }); + const lineData = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { + return [d[0], d[1]]; + }); + const scaleToDataExtent = boolean('scale to extent', true); + const y0AccessorFormat = text('y0AccessorFormat', ''); + const y1AccessorFormat = text('y1AccessorFormat', ''); + return ( + + + + Number(d).toFixed(2)} + /> + + + + + + ); +}; diff --git a/stories/area/14_stacked_band.tsx b/stories/area/14_stacked_band.tsx new file mode 100644 index 0000000000..f80f067b1d --- /dev/null +++ b/stories/area/14_stacked_band.tsx @@ -0,0 +1,55 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data; + const data2 = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => [d[0], 20, 10]); + const scaleToDataExtent = boolean('scale to extent', false); + + return ( + + + + Number(d).toFixed(2)} + /> + + + + + + ); +}; diff --git a/stories/area/15_stacked_grouped.tsx b/stories/area/15_stacked_grouped.tsx new file mode 100644 index 0000000000..543df1e1ae --- /dev/null +++ b/stories/area/15_stacked_grouped.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const data1 = [ + [1, 2], + [2, 2], + [3, 3], + [4, 5], + [5, 5], + [6, 3], + [7, 8], + [8, 2], + [9, 1], + ]; + const data2 = [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 4], + [7, 3], + [8, 2], + [9, 4], + ]; + const data3 = [ + [1, 6], + [2, 6], + [3, 3], + [4, 2], + [5, 1], + [6, 1], + [7, 5], + [8, 6], + [9, 7], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/1_basic.tsx b/stories/area/1_basic.tsx new file mode 100644 index 0000000000..e085941419 --- /dev/null +++ b/stories/area/1_basic.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { AreaSeries, Chart, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data; + return ( + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/2_with_time.tsx b/stories/area/2_with_time.tsx new file mode 100644 index 0000000000..8361067573 --- /dev/null +++ b/stories/area/2_with_time.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + return ( + + + Number(d).toFixed(2)} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/3_with_linear.tsx b/stories/area/3_with_linear.tsx new file mode 100644 index 0000000000..dec5034023 --- /dev/null +++ b/stories/area/3_with_linear.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, CurveType, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const start = KIBANA_METRICS.metrics.kibana_os_load[0].data[0][0]; + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 20).map((d) => { + return [(d[0] - start) / 30000, d[1]]; + }); + return ( + + + Number(d).toFixed(2)} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/4_with_log.tsx b/stories/area/4_with_log.tsx new file mode 100644 index 0000000000..5c88ed26d4 --- /dev/null +++ b/stories/area/4_with_log.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, CurveType, Position, ScaleType, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { + return d[1] < 7 ? [d[0], null] : [d[0], d[1] - 10]; + }); + return ( + + + Number(d).toFixed(2)} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/5_with_4_axes.tsx b/stories/area/5_with_4_axes.tsx new file mode 100644 index 0000000000..af4792abe5 --- /dev/null +++ b/stories/area/5_with_4_axes.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(0)}%`} + /> + + `${Number(d).toFixed(0)} %`} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/6_with_axis_and_legend.tsx b/stories/area/6_with_axis_and_legend.tsx new file mode 100644 index 0000000000..ea3d605418 --- /dev/null +++ b/stories/area/6_with_axis_and_legend.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/7_stacked.tsx b/stories/area/7_stacked.tsx new file mode 100644 index 0000000000..4c667daea1 --- /dev/null +++ b/stories/area/7_stacked.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + const data1 = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { + return [...d, KIBANA_METRICS.metrics.kibana_os_load[0].metric.label]; + }); + const data2 = KIBANA_METRICS.metrics.kibana_os_load[1].data.map((d) => { + return [...d, KIBANA_METRICS.metrics.kibana_os_load[1].metric.label]; + }); + const data3 = KIBANA_METRICS.metrics.kibana_os_load[2].data.map((d) => { + return [...d, KIBANA_METRICS.metrics.kibana_os_load[2].metric.label]; + }); + const allMetrics = [...data3, ...data2, ...data1]; + return ( + + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/8_stacked_percentage.tsx b/stories/area/8_stacked_percentage.tsx new file mode 100644 index 0000000000..f2f066b544 --- /dev/null +++ b/stories/area/8_stacked_percentage.tsx @@ -0,0 +1,40 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + const stackedAsPercentage = boolean('stacked as percentage', true); + return ( + + + + `${Number(d * 100).toFixed(0)} %`} + /> + + + + ); +}; diff --git a/stories/area/9_stacked_separate_specs.tsx b/stories/area/9_stacked_separate_specs.tsx new file mode 100644 index 0000000000..6b1075f81f --- /dev/null +++ b/stories/area/9_stacked_separate_specs.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm'); + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} + /> + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/area/area.stories.tsx b/stories/area/area.stories.tsx new file mode 100644 index 0000000000..4c19b60f78 --- /dev/null +++ b/stories/area/area.stories.tsx @@ -0,0 +1,25 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Area Chart', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as basic } from './1_basic'; +export { example as withTimeXAxis } from './2_with_time'; +export { example as withLinearXAxis } from './3_with_linear'; +export { example as withLogYAxis } from './4_with_log'; +export { example as with4Axes } from './5_with_4_axes'; +export { example as withAxisAndLegend } from './6_with_axis_and_legend'; +export { example as stacked } from './7_stacked'; +export { example as stackedPercentage } from './8_stacked_percentage'; +export { example as stackedSeparateSpecs } from './9_stacked_separate_specs'; +export { example as stackedSameNaming } from './10_stacked_same_naming'; +export { example as bandArea } from './13_band_area'; +export { example as stackedBand } from './14_stacked_band'; +export { example as stackedGrouped } from './15_stacked_grouped'; + +export { example as testLinear } from './11_test_linear'; +export { example as testTime } from './12_test_time'; diff --git a/stories/area_chart.tsx b/stories/area_chart.tsx deleted file mode 100644 index a9c940d6cb..0000000000 --- a/stories/area_chart.tsx +++ /dev/null @@ -1,683 +0,0 @@ -import { boolean, text } from '@storybook/addon-knobs'; -import { DateTime } from 'luxon'; -import React from 'react'; -import { - AreaSeries, - Axis, - Chart, - CurveType, - getAxisId, - getSpecId, - LineSeries, - Position, - ScaleType, - Settings, - timeFormatter, -} from '../src'; -import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { getRandomNumberGenerator } from '../src/mocks/utils'; - -const dateFormatter = timeFormatter('HH:mm'); - -export default { - title: 'Area Chart', - parameters: { - info: { - source: false, - }, - }, -}; - -export const basic = () => { - const toggleSpec = boolean('toggle area spec', true); - const data1 = KIBANA_METRICS.metrics.kibana_os_load[0].data; - const data2 = data1.map((datum) => [datum[0], datum[1] - 1]); - const data = toggleSpec ? data1 : data2; - const specId = toggleSpec ? 'areas1' : 'areas2'; - - return ( - - - - ); -}; -basic.story = { - name: 'basic', -}; - -export const withTimeXAxis = () => { - return ( - - - Number(d).toFixed(2)} - /> - - - - ); -}; -withTimeXAxis.story = { - name: 'with time x axis', -}; - -export const withLinearXAxis = () => { - const start = KIBANA_METRICS.metrics.kibana_os_load[0].data[0][0]; - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 20).map((d) => { - return [(d[0] - start) / 30000, d[1]]; - }); - return ( - - - Number(d).toFixed(2)} - /> - - - - ); -}; -withLinearXAxis.story = { - name: 'with linear x axis', -}; - -export const withLogYAxis = () => { - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { - return d[1] < 7 ? [d[0], null] : [d[0], d[1] - 10]; - }); - return ( - - - Number(d).toFixed(2)} - /> - - - - ); -}; -withLogYAxis.story = { - name: 'with log y axis', -}; - -export const with4Axes = () => { - return ( - - - - `${Number(d).toFixed(0)}%`} - /> - - `${Number(d).toFixed(0)} %`} - /> - - - - ); -}; -with4Axes.story = { - name: 'with 4 axes', -}; - -export const wAxisAndLegend = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -wAxisAndLegend.story = { - name: 'w axis and legend', -}; - -export const stackedWAxisAndLegend = () => { - const data1 = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { - return [...d, KIBANA_METRICS.metrics.kibana_os_load[0].metric.label]; - }); - const data2 = KIBANA_METRICS.metrics.kibana_os_load[1].data.map((d) => { - return [...d, KIBANA_METRICS.metrics.kibana_os_load[1].metric.label]; - }); - const data3 = KIBANA_METRICS.metrics.kibana_os_load[2].data.map((d) => { - return [...d, KIBANA_METRICS.metrics.kibana_os_load[2].metric.label]; - }); - const allMetrics = [...data3, ...data2, ...data1]; - return ( - - - - Number(d).toFixed(2)} - /> - - - ); -}; -stackedWAxisAndLegend.story = { - name: 'stacked w axis and legend', -}; - -export const stackedAsPercentage = () => { - const stackedAsPercentage = boolean('stacked as percentage', true); - return ( - - - - `${Number(d * 100).toFixed(0)} %`} - /> - - - - ); -}; -stackedAsPercentage.story = { - name: 'stacked as percentage', -}; - -export const stackedWithSeparatedSpecs = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -stackedWithSeparatedSpecs.story = { - name: 'stacked with separated specs', -}; - -export const stackedWithSeparatedSpecsSameNaming = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -stackedWithSeparatedSpecsSameNaming.story = { - name: 'stacked with separated specs - same naming', -}; - -export const testLinear = () => { - const data = [ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 4], - [7, 3], - [8, 2], - [9, 1], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testLinear.story = { - name: '[test] - linear', -}; - -export const testTime = () => { - const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); - const data = [ - [start.toMillis(), 1], - [start.plus({ minute: 1 }).toMillis(), 2], - [start.plus({ minute: 2 }).toMillis(), 3], - [start.plus({ minute: 3 }).toMillis(), 4], - [start.plus({ minute: 4 }).toMillis(), 5], - [start.plus({ minute: 5 }).toMillis(), 4], - [start.plus({ minute: 6 }).toMillis(), 3], - [start.plus({ minute: 7 }).toMillis(), 2], - [start.plus({ minute: 8 }).toMillis(), 1], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testTime.story = { - name: '[test] - time', -}; - -export const bandAreaChart = () => { - const getRandomNumber = getRandomNumberGenerator(); - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { - return { - x: d[0], - max: d[1] + 4 + 4 * getRandomNumber(), - min: d[1] - 4 - 4 * getRandomNumber(), - }; - }); - const lineData = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => { - return [d[0], d[1]]; - }); - const scaleToDataExtent = boolean('scale to extent', true); - const y0AccessorFormat = text('y0AccessorFormat', ''); - const y1AccessorFormat = text('y1AccessorFormat', ''); - return ( - - - - Number(d).toFixed(2)} - /> - - - - - - ); -}; -bandAreaChart.story = { - name: 'band area chart', -}; - -export const stackedBandAreaChart = () => { - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data; - const data2 = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d) => [d[0], 20, 10]); - const scaleToDataExtent = boolean('scale to extent', false); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - - ); -}; -stackedBandAreaChart.story = { - name: 'stacked band area chart', -}; - -export const stackedOnlyGroupedAreas = () => { - const data1 = [ - [1, 2], - [2, 2], - [3, 3], - [4, 5], - [5, 5], - [6, 3], - [7, 8], - [8, 2], - [9, 1], - ]; - const data2 = [ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 4], - [7, 3], - [8, 2], - [9, 4], - ]; - const data3 = [ - [1, 6], - [2, 6], - [3, 3], - [4, 2], - [5, 1], - [6, 1], - [7, 5], - [8, 6], - [9, 7], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -stackedOnlyGroupedAreas.story = { - name: 'stacked only grouped areas', -}; diff --git a/stories/axes/10_one_domain_bound.tsx b/stories/axes/10_one_domain_bound.tsx new file mode 100644 index 0000000000..e1a14aad15 --- /dev/null +++ b/stories/axes/10_one_domain_bound.tsx @@ -0,0 +1,41 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + const leftDomain = { + min: number('left min', 0), + }; + + const xDomain = { + max: number('xDomain max', 3), + }; + + return ( + + + + Number(d).toFixed(2)} + domain={leftDomain} + /> + + + ); +}; diff --git a/stories/axes/11_fit_domain_extent.tsx b/stories/axes/11_fit_domain_extent.tsx new file mode 100644 index 0000000000..bf19c15a4b --- /dev/null +++ b/stories/axes/11_fit_domain_extent.tsx @@ -0,0 +1,53 @@ +import { boolean, select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, Chart, LineSeries, Position, ScaleType } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; + +export const example = () => { + const dg = new SeededDataGenerator(); + const base = dg.generateBasicSeries(100, 0, 50); + const positive = base.map(({ x, y }) => ({ x, y: y + 1000 })); + const both = base.map(({ x, y }) => ({ x, y: y - 100 })); + const negative = base.map(({ x, y }) => ({ x, y: y - 1000 })); + + const dataTypes = { + positive, + both, + negative, + }; + const dataKey = select<'positive' | 'negative' | 'both'>( + 'dataset', + { + 'Positive values only': 'positive', + 'Positive and negative': 'both', + 'Negtive values only': 'negative', + }, + 'both', + ); + + const dataset = dataTypes[dataKey]; + const fit = boolean('fit domain to data', true); + + return ( + + + Number(d).toFixed(2)} + /> + + + + ); +}; diff --git a/stories/axes/1_basic.tsx b/stories/axes/1_basic.tsx new file mode 100644 index 0000000000..a35f661c05 --- /dev/null +++ b/stories/axes/1_basic.tsx @@ -0,0 +1,70 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, niceTimeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +export const example = () => { + const customStyle = { + tickLabelPadding: number('Tick Label Padding', 0, { + range: true, + min: 2, + max: 30, + step: 1, + }), + }; + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 60); + return ( + + + + Number(d).toFixed(2)} + style={customStyle} + showOverlappingLabels={boolean('Left overlap labels', false, 'Left Axis')} + showOverlappingTicks={boolean('Left overlap ticks', true, 'Left Axis')} + ticks={number( + 'Number of ticks on left', + 10, + { + range: true, + min: 2, + max: 20, + step: 1, + }, + 'Left Axis', + )} + /> + + + + ); +}; diff --git a/stories/axes/2_tick_label_rotation.tsx b/stories/axes/2_tick_label_rotation.tsx new file mode 100644 index 0000000000..eb19c0fb06 --- /dev/null +++ b/stories/axes/2_tick_label_rotation.tsx @@ -0,0 +1,85 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + const customStyle = { + tickLabelPadding: number('Tick Label Padding', 0), + }; + + return ( + + + Number(d).toFixed(2)} + style={customStyle} + hide={boolean('hide left axis', false)} + /> + Number(d).toFixed(2)} + style={customStyle} + hide={boolean('hide top axis', false)} + /> + Number(d).toFixed(2)} + style={customStyle} + hide={boolean('hide right axis', false)} + /> + + + + ); +}; diff --git a/stories/axes/3_axis_4_axes.tsx b/stories/axes/3_axis_4_axes.tsx new file mode 100644 index 0000000000..e1edf438e7 --- /dev/null +++ b/stories/axes/3_axis_4_axes.tsx @@ -0,0 +1,53 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AreaSeries, Axis, Chart, Position, ScaleType } from '../../src'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} + hide={boolean('hide left axis', false)} + /> + + Number(d).toFixed(2)} + hide={boolean('hide right axis', false)} + /> + + + + ); +}; diff --git a/stories/axes/4_multi_axis.tsx b/stories/axes/4_multi_axis.tsx new file mode 100644 index 0000000000..10f18c5eae --- /dev/null +++ b/stories/axes/4_multi_axis.tsx @@ -0,0 +1,94 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { + Axis, + BarSeries, + Chart, + LIGHT_THEME, + mergeWithDefaultTheme, + PartialTheme, + Position, + ScaleType, + Settings, +} from '../../src'; + +function createThemeAction(title: string, min: number, max: number, value: number) { + return number( + title, + value, + { + range: true, + min, + max, + step: 1, + }, + 'theme', + ); +} + +function renderAxisWithOptions(position: Position, seriesGroup: string, show: boolean) { + const axisTitle = `${position} axis (${seriesGroup})`; + + const showAxis = boolean(`show ${axisTitle} axis`, show, `${position} axes`); + + if (!showAxis) { + return null; + } + + const axisProps = { + id: axisTitle, + position, + title: axisTitle, + showOverlappingTicks: true, + }; + + return ; +} + +export const example = () => { + const theme: PartialTheme = { + chartMargins: { + left: createThemeAction('margin left', 0, 50, 0), + right: createThemeAction('margin right', 0, 50, 0), + top: createThemeAction('margin top', 0, 50, 0), + bottom: createThemeAction('margin bottom', 0, 50, 0), + }, + chartPaddings: { + left: createThemeAction('padding left', 0, 50, 0), + right: createThemeAction('padding right', 0, 50, 0), + top: createThemeAction('padding top', 0, 50, 0), + bottom: createThemeAction('padding bottom', 0, 50, 0), + }, + }; + const customTheme = mergeWithDefaultTheme(theme, LIGHT_THEME); + + const seriesGroup1 = 'group1'; + const seriesGroup2 = 'group2'; + return ( + + + {renderAxisWithOptions(Position.Top, seriesGroup1, false)} + {renderAxisWithOptions(Position.Top, seriesGroup2, true)} + {renderAxisWithOptions(Position.Left, seriesGroup1, false)} + {renderAxisWithOptions(Position.Left, seriesGroup2, true)} + {renderAxisWithOptions(Position.Bottom, seriesGroup1, false)} + {renderAxisWithOptions(Position.Bottom, seriesGroup2, true)} + {renderAxisWithOptions(Position.Right, seriesGroup1, false)} + {renderAxisWithOptions(Position.Right, seriesGroup2, true)} + + + ); +}; diff --git a/stories/axes/5_multi_axis_bar_lines.tsx b/stories/axes/5_multi_axis_bar_lines.tsx new file mode 100644 index 0000000000..fadb9e035d --- /dev/null +++ b/stories/axes/5_multi_axis_bar_lines.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + Number(d).toFixed(2)} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/axes/6_different_tooltip.tsx b/stories/axes/6_different_tooltip.tsx new file mode 100644 index 0000000000..3bb04d31c6 --- /dev/null +++ b/stories/axes/6_different_tooltip.tsx @@ -0,0 +1,66 @@ +import React from 'react'; + +import { Axis, Chart, LineSeries, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + `${Number(d).toFixed(2)} %`} + /> + `${Number(d).toFixed(2)}/s`} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/axes/7_many_tick_labels.tsx b/stories/axes/7_many_tick_labels.tsx new file mode 100644 index 0000000000..db7d2baee0 --- /dev/null +++ b/stories/axes/7_many_tick_labels.tsx @@ -0,0 +1,34 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; + +export const example = () => { + const dg = new SeededDataGenerator(); + const data = dg.generateSimpleSeries(31); + const customStyle = { + tickLabelPadding: number('Tick Label Padding', 0), + }; + + return ( + + + + + + ); +}; diff --git a/stories/axes/8_custom_domain.tsx b/stories/axes/8_custom_domain.tsx new file mode 100644 index 0000000000..c946758475 --- /dev/null +++ b/stories/axes/8_custom_domain.tsx @@ -0,0 +1,85 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + const leftDomain = { + min: number('left min', 0), + max: number('left max', 7), + }; + + const rightDomain1 = { + min: number('right1 min', 0), + max: number('right1 max', 10), + }; + + const rightDomain2 = { + min: number('right2 min', 0), + max: number('right2 max', 10), + }; + + const xDomain = { + min: number('xDomain min', 0), + max: number('xDomain max', 3), + }; + return ( + + + + Number(d).toFixed(2)} + domain={leftDomain} + hide={boolean('hide left axis', false)} + /> + Number(d).toFixed(2)} + domain={rightDomain1} + /> + Number(d).toFixed(2)} + domain={rightDomain2} + /> + + + + ); +}; diff --git a/stories/axes/9_custom_mixed_domain.tsx b/stories/axes/9_custom_mixed_domain.tsx new file mode 100644 index 0000000000..d731c272ef --- /dev/null +++ b/stories/axes/9_custom_mixed_domain.tsx @@ -0,0 +1,77 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src'; +import { arrayKnobs } from '../utils/knobs'; + +export const example = () => { + const leftDomain = { + min: number('left min', 0), + max: number('left max', 7), + }; + + const right1Domain = { + min: number('right1 min', 0), + max: number('right1 max', 10), + }; + + const xDomain = arrayKnobs('xDomain', ['a', 'b', 'c', 'd', 0, 1, 2, 3]); + + return ( + + + + Number(d).toFixed(2)} + domain={leftDomain} + /> + Number(d).toFixed(2)} + domain={right1Domain} + /> + Number(d).toFixed(2)} + /> + + + + ); +}; diff --git a/stories/axes/axes.stories.tsx b/stories/axes/axes.stories.tsx new file mode 100644 index 0000000000..0abf981465 --- /dev/null +++ b/stories/axes/axes.stories.tsx @@ -0,0 +1,20 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Axes', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as basic } from './1_basic'; +export { example as tickLabelRotation } from './2_tick_label_rotation'; +export { example as with4Axes } from './3_axis_4_axes'; +export { example as multiAxes } from './4_multi_axis'; +export { example as barsAndLines } from './5_multi_axis_bar_lines'; +export { example as differentTooltip } from './6_different_tooltip'; +export { example as manyTickLabels } from './7_many_tick_labels'; +export { example as customDomain } from './8_custom_domain'; +export { example as customMixed } from './9_custom_mixed_domain'; +export { example as oneDomainBound } from './10_one_domain_bound'; +export { example as fitDomain } from './11_fit_domain_extent'; diff --git a/stories/axis.tsx b/stories/axis.tsx deleted file mode 100644 index 668a234181..0000000000 --- a/stories/axis.tsx +++ /dev/null @@ -1,714 +0,0 @@ -import { boolean, number, select } from '@storybook/addon-knobs'; -import React from 'react'; - -import { - AreaSeries, - Axis, - BarSeries, - Chart, - getAxisId, - getGroupId, - getSpecId, - LIGHT_THEME, - LineSeries, - mergeWithDefaultTheme, - PartialTheme, - Position, - ScaleType, - Settings, - niceTimeFormatter, -} from '../src/'; -import { SeededDataGenerator } from '../src/mocks/utils'; -import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { arrayKnobs } from './common'; - -function createThemeAction(title: string, min: number, max: number, value: number) { - return number( - title, - value, - { - range: true, - min, - max, - step: 1, - }, - 'theme', - ); -} - -function renderAxisWithOptions(position: Position, seriesGroup: string, show: boolean) { - const axisTitle = `${position} axis (${seriesGroup})`; - - const showAxis = boolean(`show ${axisTitle} axis`, show, `${position} axes`); - - if (!showAxis) { - return null; - } - - const axisProps = { - id: getAxisId(axisTitle), - position, - title: axisTitle, - showOverlappingTicks: true, - }; - - return ; -} - -export default { - title: 'Axis', - parameters: { - info: { - source: false, - }, - }, -}; - -export const basic = () => { - const customStyle = { - tickLabelPadding: number('Tick Label Padding', 0, { - range: true, - min: 2, - max: 30, - step: 1, - }), - }; - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 60); - return ( - - - - Number(d).toFixed(2)} - style={customStyle} - showOverlappingLabels={boolean('Left overlap labels', false, 'Left Axis')} - showOverlappingTicks={boolean('Left overlap ticks', true, 'Left Axis')} - ticks={number( - 'Number of ticks on left', - 10, - { - range: true, - min: 2, - max: 20, - step: 1, - }, - 'Left Axis', - )} - /> - - - - ); -}; -basic.story = { - name: 'basic', -}; - -export const tickLabelRotation = () => { - const customStyle = { - tickLabelPadding: number('Tick Label Padding', 0), - }; - - return ( - - - Number(d).toFixed(2)} - style={customStyle} - hide={boolean('hide left axis', false)} - /> - Number(d).toFixed(2)} - style={customStyle} - hide={boolean('hide top axis', false)} - /> - Number(d).toFixed(2)} - style={customStyle} - hide={boolean('hide right axis', false)} - /> - - - - ); -}; -tickLabelRotation.story = { - name: 'tick label rotation', -}; - -export const axis4axes = () => { - return ( - - - Number(d).toFixed(2)} - hide={boolean('hide left axis', false)} - /> - - Number(d).toFixed(2)} - hide={boolean('hide right axis', false)} - /> - - - - ); -}; -axis4axes.story = { - name: '4 axes', -}; - -export const withMultiAxis = () => { - const theme: PartialTheme = { - chartMargins: { - left: createThemeAction('margin left', 0, 50, 0), - right: createThemeAction('margin right', 0, 50, 0), - top: createThemeAction('margin top', 0, 50, 0), - bottom: createThemeAction('margin bottom', 0, 50, 0), - }, - chartPaddings: { - left: createThemeAction('padding left', 0, 50, 0), - right: createThemeAction('padding right', 0, 50, 0), - top: createThemeAction('padding top', 0, 50, 0), - bottom: createThemeAction('padding bottom', 0, 50, 0), - }, - }; - const customTheme = mergeWithDefaultTheme(theme, LIGHT_THEME); - - const seriesGroup1 = 'group1'; - const seriesGroup2 = 'group2'; - return ( - - - {renderAxisWithOptions(Position.Top, seriesGroup1, false)} - {renderAxisWithOptions(Position.Top, seriesGroup2, true)} - {renderAxisWithOptions(Position.Left, seriesGroup1, false)} - {renderAxisWithOptions(Position.Left, seriesGroup2, true)} - {renderAxisWithOptions(Position.Bottom, seriesGroup1, false)} - {renderAxisWithOptions(Position.Bottom, seriesGroup2, true)} - {renderAxisWithOptions(Position.Right, seriesGroup1, false)} - {renderAxisWithOptions(Position.Right, seriesGroup2, true)} - - - ); -}; -withMultiAxis.story = { - parameter: 'with multi axis', -}; - -export const withMultiAxisBarLines = () => { - return ( - - - - Number(d).toFixed(2)} - /> - Number(d).toFixed(2)} - /> - - - - ); -}; -withMultiAxisBarLines.story = { - name: 'with multi axis bar/lines', -}; - -export const withMultiAxisDifferentTooltipFormats = () => { - return ( - - - - `${Number(d).toFixed(2)} %`} - /> - `${Number(d).toFixed(2)}/s`} - /> - - - - ); -}; -withMultiAxisDifferentTooltipFormats.story = { - name: 'with multi axis different tooltip formats', -}; - -export const wManyTickLabels = () => { - const dg = new SeededDataGenerator(); - const data = dg.generateSimpleSeries(31); - const customStyle = { - tickLabelPadding: number('Tick Label Padding', 0), - }; - - return ( - - - - - - ); -}; -wManyTickLabels.story = { - name: 'w many tick labels', -}; - -export const customizingDomainLimits = () => { - const leftDomain = { - min: number('left min', 0), - max: number('left max', 7), - }; - - const rightDomain1 = { - min: number('right1 min', 0), - max: number('right1 max', 10), - }; - - const rightDomain2 = { - min: number('right2 min', 0), - max: number('right2 max', 10), - }; - - const xDomain = { - min: number('xDomain min', 0), - max: number('xDomain max', 3), - }; - return ( - - - - Number(d).toFixed(2)} - domain={leftDomain} - hide={boolean('hide left axis', false)} - /> - Number(d).toFixed(2)} - domain={rightDomain1} - /> - Number(d).toFixed(2)} - domain={rightDomain2} - /> - - - - ); -}; -customizingDomainLimits.story = { - name: 'customizing domain limits [mixed linear chart]', -}; - -export const customizingDomainLimitsMixedOrdinalLinearXDomain = () => { - const leftDomain = { - min: number('left min', 0), - max: number('left max', 7), - }; - - const right1Domain = { - min: number('right1 min', 0), - max: number('right1 max', 10), - }; - - const xDomain = arrayKnobs('xDomain', ['a', 'b', 'c', 'd', 0, 1, 2, 3]); - - return ( - - - - Number(d).toFixed(2)} - domain={leftDomain} - /> - Number(d).toFixed(2)} - domain={right1Domain} - /> - Number(d).toFixed(2)} - /> - - - - ); -}; -customizingDomainLimitsMixedOrdinalLinearXDomain.story = { - name: 'customizing domain limits [mixed ordinal & linear x domain]', -}; - -export const customizingDomainLimitsOnlyOneBoundDefined = () => { - const leftDomain = { - min: number('left min', 0), - }; - - const xDomain = { - max: number('xDomain max', 3), - }; - - return ( - - - - Number(d).toFixed(2)} - domain={leftDomain} - /> - - - ); -}; -customizingDomainLimitsOnlyOneBoundDefined.story = { - name: 'customizing domain limits [only one bound defined]', -}; - -export const fitDomainToExtentInYAxis = () => { - const dg = new SeededDataGenerator(); - const base = dg.generateBasicSeries(100, 0, 50); - const positive = base.map(({ x, y }) => ({ x, y: y + 1000 })); - const both = base.map(({ x, y }) => ({ x, y: y - 100 })); - const negative = base.map(({ x, y }) => ({ x, y: y - 1000 })); - - const dataTypes = { - positive, - both, - negative, - }; - const dataKey = select( - 'dataset', - { - 'Positive values only': 'positive', - 'Positive and negative': 'both', - 'Negtive values only': 'negative', - }, - 'both', - ); - // @ts-ignore - const dataset = dataTypes[dataKey]; - const fit = boolean('fit domain to data', true); - - return ( - - - Number(d).toFixed(2)} - /> - - - - ); -}; -fitDomainToExtentInYAxis.story = { - name: 'fit domain to extent in y axis', -}; diff --git a/stories/bar/10_axis_and_legend.tsx b/stories/bar/10_axis_and_legend.tsx new file mode 100644 index 0000000000..fb85c29c05 --- /dev/null +++ b/stories/bar/10_axis_and_legend.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/11_stacked_with_axis_and_legend.tsx b/stories/bar/11_stacked_with_axis_and_legend.tsx new file mode 100644 index 0000000000..33f7af1938 --- /dev/null +++ b/stories/bar/11_stacked_with_axis_and_legend.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/12_stacked_as_percentage.tsx b/stories/bar/12_stacked_as_percentage.tsx new file mode 100644 index 0000000000..98deb42b4d --- /dev/null +++ b/stories/bar/12_stacked_as_percentage.tsx @@ -0,0 +1,50 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const stackedAsPercentage = boolean('stacked as percentage', true); + const clusterBars = boolean('cluster', true); + return ( + + + + (stackedAsPercentage && !clusterBars ? `${Number(d * 100).toFixed(0)} %` : d)} + /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/13_clustered.tsx b/stories/bar/13_clustered.tsx new file mode 100644 index 0000000000..5ec198e017 --- /dev/null +++ b/stories/bar/13_clustered.tsx @@ -0,0 +1,57 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, LIGHT_THEME, Position, ScaleType, Settings } from '../../src'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const theme = { + ...LIGHT_THEME, + scales: { + histogramPadding: number('histogram padding', 0.05, { + range: true, + min: 0, + max: 1, + step: 0.01, + }), + barsPadding: number('bar padding', 0.25, { + range: true, + min: 0, + max: 1, + step: 0.01, + }), + }, + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/14_clustered_multiple.tsx b/stories/bar/14_clustered_multiple.tsx new file mode 100644 index 0000000000..87fcaeb011 --- /dev/null +++ b/stories/bar/14_clustered_multiple.tsx @@ -0,0 +1,66 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/15_time_clustered.tsx b/stories/bar/15_time_clustered.tsx new file mode 100644 index 0000000000..85e0ea8a5f --- /dev/null +++ b/stories/bar/15_time_clustered.tsx @@ -0,0 +1,48 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, niceTimeFormatByDay, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +export const example = () => { + const formatter = timeFormatter(niceTimeFormatByDay(1)); + return ( + + + + Number(d).toFixed(2)} /> + + + + + + ); +}; diff --git a/stories/bar/17_time_stacked.tsx b/stories/bar/17_time_stacked.tsx new file mode 100644 index 0000000000..fa11a7a53f --- /dev/null +++ b/stories/bar/17_time_stacked.tsx @@ -0,0 +1,51 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, niceTimeFormatByDay, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +export const example = () => { + const formatter = timeFormatter(niceTimeFormatByDay(1)); + return ( + + + + Number(d).toFixed(2)} /> + + + + + + ); +}; diff --git a/stories/bar/18_bar_chart_1y0g.tsx b/stories/bar/18_bar_chart_1y0g.tsx new file mode 100644 index 0000000000..9a6263d700 --- /dev/null +++ b/stories/bar/18_bar_chart_1y0g.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/19_bar_chart_1y1g.tsx b/stories/bar/19_bar_chart_1y1g.tsx new file mode 100644 index 0000000000..808b9cd91e --- /dev/null +++ b/stories/bar/19_bar_chart_1y1g.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/1_basic.tsx b/stories/bar/1_basic.tsx new file mode 100644 index 0000000000..7745a2f075 --- /dev/null +++ b/stories/bar/1_basic.tsx @@ -0,0 +1,32 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { BarSeries, Chart, ScaleType } from '../../src'; + +export const example = () => { + const darkmode = boolean('darkmode', false); + const className = darkmode ? 'story-chart-dark' : 'story-chart'; + const toggleSpec = boolean('toggle bar spec', true); + const data1 = [ + { x: 0, y: 2 }, + { x: 1, y: 7 }, + { x: 2, y: 3 }, + { x: 3, y: 6 }, + ]; + const data2 = data1.map((datum) => ({ ...datum, y: datum.y - 1 })); + const data = toggleSpec ? data1 : data2; + const specId = toggleSpec ? 'bars1' : 'bars2'; + return ( + + + + ); +}; diff --git a/stories/bar/20_bar_chart_1y2g.tsx b/stories/bar/20_bar_chart_1y2g.tsx new file mode 100644 index 0000000000..76fcac92de --- /dev/null +++ b/stories/bar/20_bar_chart_1y2g.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/21_bar_chart_2y0g.tsx b/stories/bar/21_bar_chart_2y0g.tsx new file mode 100644 index 0000000000..1e70f769c3 --- /dev/null +++ b/stories/bar/21_bar_chart_2y0g.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/22_barchart_2y1g.tsx b/stories/bar/22_barchart_2y1g.tsx new file mode 100644 index 0000000000..499d93a4a6 --- /dev/null +++ b/stories/bar/22_barchart_2y1g.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/23_bar_chart_2y2g.tsx b/stories/bar/23_bar_chart_2y2g.tsx new file mode 100644 index 0000000000..6e1b04d377 --- /dev/null +++ b/stories/bar/23_bar_chart_2y2g.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { FilterPredicate } from '../../src/chart_types/xy_chart/utils/specs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const isVisibleFunction: FilterPredicate = (series) => { + return series.splitAccessors.size > 0 + ? series.specId === 'bars1' && series.yAccessor === 'y1' && series.splitAccessors.get('g1') === 'cloudflare.com' + : series.specId === 'bars1' && series.yAccessor === 'y1'; + }; + + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/24_tooltip_visibility.tsx b/stories/bar/24_tooltip_visibility.tsx new file mode 100644 index 0000000000..562a9c10f2 --- /dev/null +++ b/stories/bar/24_tooltip_visibility.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { FilterPredicate } from '../../src/chart_types/xy_chart/utils/specs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const isVisibleFunction: FilterPredicate = (series) => { + return series.splitAccessors.get('g1') === 'cloudflare.com'; + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/25_high_data_volume.tsx b/stories/bar/25_high_data_volume.tsx new file mode 100644 index 0000000000..f3dcaf1a42 --- /dev/null +++ b/stories/bar/25_high_data_volume.tsx @@ -0,0 +1,37 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, TooltipType } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const dg = new SeededDataGenerator(); + const data = dg.generateSimpleSeries(15000); + const tooltipProps = { + type: TooltipType.Follow, + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/26_single_data_linear.tsx b/stories/bar/26_single_data_linear.tsx new file mode 100644 index 0000000000..9046fbcd91 --- /dev/null +++ b/stories/bar/26_single_data_linear.tsx @@ -0,0 +1,42 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const hasCustomDomain = boolean('has custom domain', false); + const xDomain = hasCustomDomain + ? { + min: 0, + } + : undefined; + + const theme = { + scales: { + barsPadding: number('bars padding', 0.25, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + }, + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/27_single_data_ordinal.tsx b/stories/bar/27_single_data_ordinal.tsx new file mode 100644 index 0000000000..c2cc770902 --- /dev/null +++ b/stories/bar/27_single_data_ordinal.tsx @@ -0,0 +1,37 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const hasCustomDomain = boolean('has custom domain', false); + const xDomain = hasCustomDomain ? ['a', 'b'] : undefined; + + const theme = { + scales: { + barsPadding: number('bars padding', 0.25, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + }, + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/28_single_data_clustered.tsx b/stories/bar/28_single_data_clustered.tsx new file mode 100644 index 0000000000..2e915390ab --- /dev/null +++ b/stories/bar/28_single_data_clustered.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/29_single_data_stacked.tsx b/stories/bar/29_single_data_stacked.tsx new file mode 100644 index 0000000000..251f2ac09e --- /dev/null +++ b/stories/bar/29_single_data_stacked.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/2_label_value.tsx b/stories/bar/2_label_value.tsx new file mode 100644 index 0000000000..d8a005f822 --- /dev/null +++ b/stories/bar/2_label_value.tsx @@ -0,0 +1,106 @@ +import { boolean, color, number, select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; +import { getChartRotationKnob } from '../utils/knobs'; + +const dataGen = new SeededDataGenerator(); +function generateDataWithAdditional(num: number) { + return [...dataGen.generateSimpleSeries(num), { x: num, y: 0.25, g: 0 }, { x: num + 1, y: 8, g: 0 }]; +} +const frozenDataSmallVolume = generateDataWithAdditional(10); +const frozenDataMediumVolume = generateDataWithAdditional(50); +const frozenDataHighVolume = generateDataWithAdditional(1500); + +const frozenData: { [key: string]: any[] } = { + s: frozenDataSmallVolume, + m: frozenDataMediumVolume, + h: frozenDataHighVolume, +}; + +export const example = () => { + const showValueLabel = boolean('show value label', true); + const isAlternatingValueLabel = boolean('alternating value label', false); + const isValueContainedInElement = boolean('contain value label within bar element', false); + const hideClippedValue = boolean('hide clipped value', false); + + const displayValueSettings = { + showValueLabel, + isAlternatingValueLabel, + isValueContainedInElement, + hideClippedValue, + }; + + const debug = boolean('debug', false); + + const theme = { + barSeriesStyle: { + displayValue: { + fontSize: number('value font size', 10), + fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontStyle: 'normal', + padding: 0, + fill: color('value color', '#000'), + offsetX: number('offsetX', 0), + offsetY: number('offsetY', 0), + }, + }, + }; + + const dataSize = select( + 'data volume size', + { + 'small volume': 's', + 'medium volume': 'm', + 'high volume': 'h', + }, + 's', + ); + const data = frozenData[dataSize]; + + const isSplitSeries = boolean('split series', false); + const isStackedSeries = boolean('stacked series', false); + + const splitSeriesAccessors = isSplitSeries ? ['g'] : undefined; + const stackAccessors = isStackedSeries ? ['x'] : undefined; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/30_stacked_to_extent.tsx b/stories/bar/30_stacked_to_extent.tsx new file mode 100644 index 0000000000..a86110360d --- /dev/null +++ b/stories/bar/30_stacked_to_extent.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/31_negative_and_positive_x_values.tsx b/stories/bar/31_negative_and_positive_x_values.tsx new file mode 100644 index 0000000000..39676e0fb8 --- /dev/null +++ b/stories/bar/31_negative_and_positive_x_values.tsx @@ -0,0 +1,39 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/32_scale_to_extent.tsx b/stories/bar/32_scale_to_extent.tsx new file mode 100644 index 0000000000..8411b136a8 --- /dev/null +++ b/stories/bar/32_scale_to_extent.tsx @@ -0,0 +1,55 @@ +import { boolean, select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; + +export const example = () => { + const yScaleToDataExtent = boolean('yScaleDataToExtent', true); + const mixed = [ + { x: 0, y: -4 }, + { x: 1, y: -3 }, + { x: 2, y: 2 }, + { x: 3, y: 1 }, + ]; + + const allPositive = mixed.map((datum) => ({ x: datum.x, y: Math.abs(datum.y) })); + const allNegative = mixed.map((datum) => ({ x: datum.x, y: Math.abs(datum.y) * -1 })); + + const dataChoice = select( + 'data', + { + mixed: 'mixed', + allPositive: 'all positive', + allNegative: 'all negative', + }, + 'all negative', + ); + + let data = mixed; + switch (dataChoice) { + case 'all positive': + data = allPositive; + break; + case 'all negative': + data = allNegative; + break; + } + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/33_band_bar.tsx b/stories/bar/33_band_bar.tsx new file mode 100644 index 0000000000..fc4d5ea793 --- /dev/null +++ b/stories/bar/33_band_bar.tsx @@ -0,0 +1,63 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, timeFormatter } from '../../src'; +import { getRandomNumberGenerator } from '../../src/mocks/utils'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter('HH:mm:ss'); + +export const example = () => { + const getRandomNumber = getRandomNumberGenerator(); + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d: any) => { + return { + x: d[0], + max: d[1] + 4 + 4 * getRandomNumber(), + min: d[1] - 4 - 4 * getRandomNumber(), + }; + }); + const lineData = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d: any) => { + return [d[0], d[1]]; + }); + const scaleToDataExtent = boolean('scale to extent', true); + return ( + + + Number(d).toFixed(2)} + /> + + + + + + ); +}; diff --git a/stories/bar/34_test_linear.tsx b/stories/bar/34_test_linear.tsx new file mode 100644 index 0000000000..61119f6893 --- /dev/null +++ b/stories/bar/34_test_linear.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 4], + [7, 3], + [8, 2], + [9, 1], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/35_test_time.tsx b/stories/bar/35_test_time.tsx new file mode 100644 index 0000000000..ebd6410a0b --- /dev/null +++ b/stories/bar/35_test_time.tsx @@ -0,0 +1,50 @@ +import { DateTime } from 'luxon'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm:ss'); + +// for testing purposes only +export const example = () => { + const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); + const data = [ + [start.toMillis(), 1], + [start.plus({ minute: 1 }).toMillis(), 2], + [start.plus({ minute: 2 }).toMillis(), 3], + [start.plus({ minute: 3 }).toMillis(), 4], + [start.plus({ minute: 4 }).toMillis(), 5], + [start.plus({ minute: 5 }).toMillis(), 4], + [start.plus({ minute: 6 }).toMillis(), 3], + [start.plus({ minute: 7 }).toMillis(), 2], + [start.plus({ minute: 8 }).toMillis(), 1], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/36_test_linear_clustered.tsx b/stories/bar/36_test_linear_clustered.tsx new file mode 100644 index 0000000000..c725bd0df4 --- /dev/null +++ b/stories/bar/36_test_linear_clustered.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data9 = [ + [1, 1, 3], + [2, 2, 4], + [3, 3, 5], + [4, 4, 6], + [5, 5, 7], + [6, 4, 6], + [7, 3, 5], + [8, 2, 4], + [9, 1, 3], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/37_test_time_clustered.tsx b/stories/bar/37_test_time_clustered.tsx new file mode 100644 index 0000000000..07b74623c4 --- /dev/null +++ b/stories/bar/37_test_time_clustered.tsx @@ -0,0 +1,50 @@ +import { DateTime } from 'luxon'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter('HH:mm:ss'); + +// for testing purposes only +export const example = () => { + const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); + const data = [ + [start.toMillis(), 1, 4], + [start.plus({ minute: 1 }).toMillis(), 2, 5], + [start.plus({ minute: 2 }).toMillis(), 3, 6], + [start.plus({ minute: 3 }).toMillis(), 4, 7], + [start.plus({ minute: 4 }).toMillis(), 5, 8], + [start.plus({ minute: 5 }).toMillis(), 4, 7], + [start.plus({ minute: 6 }).toMillis(), 3, 6], + [start.plus({ minute: 7 }).toMillis(), 2, 5], + [start.plus({ minute: 8 }).toMillis(), 1, 4], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/38_test_clustered_null_bars.tsx b/stories/bar/38_test_clustered_null_bars.tsx new file mode 100644 index 0000000000..c8f8845d7a --- /dev/null +++ b/stories/bar/38_test_clustered_null_bars.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = [ + [1, 1, 3, 'a'], + [2, null, 4, 'a'], + [3, 3, 5, 'a'], + [4, 4, 6, 'a'], + [1, 1, 3, 'b'], + [2, 2, 4, 'b'], + [3, 3, 5, 'b'], + [4, 4, 6, 'b'], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/39_test_stacked_null.tsx b/stories/bar/39_test_stacked_null.tsx new file mode 100644 index 0000000000..050e8fb127 --- /dev/null +++ b/stories/bar/39_test_stacked_null.tsx @@ -0,0 +1,47 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = [ + [1, 1, 3, 'a'], + [2, null, 4, 'a'], + [3, 3, 5, 'a'], + [4, 4, 6, 'a'], + [1, 1, 3, 'b'], + [2, 2, 4, 'b'], + [3, null, 5, 'b'], + [4, 4, 6, 'b'], + ]; + return ( + + + Number(d).toFixed(2)} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/3_with_axis.tsx b/stories/bar/3_with_axis.tsx new file mode 100644 index 0000000000..edbaee9fb8 --- /dev/null +++ b/stories/bar/3_with_axis.tsx @@ -0,0 +1,31 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, DARK_THEME, LIGHT_THEME, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + const darkmode = boolean('darkmode', false); + const className = darkmode ? 'story-chart-dark' : 'story-chart'; + const defaultTheme = darkmode ? DARK_THEME : LIGHT_THEME; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/40_test_switch.tsx b/stories/bar/40_test_switch.tsx new file mode 100644 index 0000000000..dbd151a7d4 --- /dev/null +++ b/stories/bar/40_test_switch.tsx @@ -0,0 +1,43 @@ +import { select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/41_test_histogram_linear.tsx b/stories/bar/41_test_histogram_linear.tsx new file mode 100644 index 0000000000..1e27b8b51c --- /dev/null +++ b/stories/bar/41_test_histogram_linear.tsx @@ -0,0 +1,158 @@ +import { boolean, number, select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { + AnnotationDomainTypes, + AreaSeries, + Axis, + BarSeries, + Chart, + HistogramBarSeries, + HistogramModeAlignments, + LineAnnotation, + LineSeries, + Position, + RectAnnotation, + ScaleType, + Settings, +} from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { getChartRotationKnob } from '../utils/knobs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = TestDatasets.BARCHART_2Y1G; + + const lineAnnotationStyle = { + line: { + strokeWidth: 2, + stroke: '#c80000', + opacity: 0.3, + }, + }; + + const theme = { + scales: { + barsPadding: number('bars padding', 0.25, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + histogramPadding: number('histogram padding', 0.05, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + }, + }; + + const otherSeriesSelection = select( + 'other series', + { + line: 'line', + area: 'area', + }, + 'line', + ); + + const pointAlignment = select('point series alignment', HistogramModeAlignments, HistogramModeAlignments.Center); + const pointData = TestDatasets.BARCHART_1Y0G; + + const otherSeries = + otherSeriesSelection === 'line' ? ( + + ) : ( + + ); + + const hasHistogramBarSeries = boolean('hasHistogramBarSeries', false); + return ( + + + } + /> + + + + {hasHistogramBarSeries && ( + + )} + + + {otherSeries} + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/42_test_histogram_ordinal.tsx b/stories/bar/42_test_histogram_ordinal.tsx new file mode 100644 index 0000000000..c31f0ddc39 --- /dev/null +++ b/stories/bar/42_test_histogram_ordinal.tsx @@ -0,0 +1,72 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, HistogramBarSeries, Position, ScaleType, Settings } from '../../src'; +import { getChartRotationKnob } from '../utils/knobs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = [ + { x: 'a', y: 2 }, + { x: 'b', y: 7 }, + { x: 'c', y: 0 }, + { x: 'd', y: 6 }, + ]; + const theme = { + scales: { + barsPadding: number('bars padding', 0.25, { + range: true, + min: 0, + max: 1, + step: 0.1, + }), + }, + }; + + const hasHistogramBarSeries = boolean('hasHistogramBarSeries', false); + return ( + + + + + {hasHistogramBarSeries && ( + + )} + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/43_test_discover.tsx b/stories/bar/43_test_discover.tsx new file mode 100644 index 0000000000..1fb8f06462 --- /dev/null +++ b/stories/bar/43_test_discover.tsx @@ -0,0 +1,58 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { + Axis, + Chart, + HistogramBarSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src'; +import { TEST_DATASET_DISCOVER } from '../../src/utils/data_samples/test_dataset_discover_per_30s'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const data = TEST_DATASET_DISCOVER.series[0].values; + + const formatter = timeFormatter(niceTimeFormatByDay(1)); + + const xDomain = { + minInterval: 30000, + }; + + const useCustomMinInterval = boolean('use custom minInterval of 30s', true); + return ( + + + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/44_test_single_histogram.tsx b/stories/bar/44_test_single_histogram.tsx new file mode 100644 index 0000000000..88c167ff2c --- /dev/null +++ b/stories/bar/44_test_single_histogram.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +import { + Axis, + Chart, + HistogramBarSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const formatter = timeFormatter(niceTimeFormatByDay(1)); + + const xDomain = { + minInterval: 60000, + }; + + return ( + + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/45_min_height.tsx b/stories/bar/45_min_height.tsx new file mode 100644 index 0000000000..ea3a06289c --- /dev/null +++ b/stories/bar/45_min_height.tsx @@ -0,0 +1,42 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const minBarHeight = number('minBarHeight', 5); + const data = [ + [1, 100000], + [2, 10000], + [3, 1000], + [4, 100], + [5, 10], + [6, 1], + [7, 0], + [8, 1], + [9, 0], + ]; + return ( + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/46_test_min_height.tsx b/stories/bar/46_test_min_height.tsx new file mode 100644 index 0000000000..d584e2af72 --- /dev/null +++ b/stories/bar/46_test_min_height.tsx @@ -0,0 +1,52 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + const minBarHeight = number('minBarHeight', 10); + const data = [ + [1, -100000], + [2, -10000], + [3, -1000], + [4, -100], + [5, -10], + [6, -1], + [7, 0], + [8, -1], + [9, 0], + [10, 0], + [11, 1], + [12, 0], + [13, 1], + [14, 10], + [15, 100], + [16, 1000], + [17, 10000], + [18, 100000], + ]; + return ( + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/47_stacked_only_grouped.tsx b/stories/bar/47_stacked_only_grouped.tsx new file mode 100644 index 0000000000..c7d30bd849 --- /dev/null +++ b/stories/bar/47_stacked_only_grouped.tsx @@ -0,0 +1,143 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const data1 = [ + [1, 2], + [2, 2], + [3, 3], + [4, 5], + [5, 5], + [6, 3], + [7, 8], + [8, 2], + [9, 1], + ]; + const data2 = [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 4], + [7, 3], + [8, 2], + [9, 4], + ]; + const data3 = [ + [1, 6], + [2, 6], + [3, 3], + [4, 2], + [5, 1], + [6, 1], + [7, 5], + [8, 6], + [9, 7], + ]; + const data4 = [ + [1, 2], + [2, 6], + [3, 2], + [4, 9], + [5, 2], + [6, 3], + [7, 1], + [8, 2], + [9, 7], + ]; + const data5 = [ + [1, 1], + [2, 7], + [3, 5], + [4, 6], + [5, 5], + [6, 4], + [7, 2], + [8, 4], + [9, 8], + ]; + return ( + + + Number(d).toFixed(2)} + domain={{ min: 0, max: 15 }} + /> + Number(d).toFixed(2)} + hide={true} + domain={{ min: 0, max: 15 }} + /> + + + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/48_test_tooltip.tsx b/stories/bar/48_test_tooltip.tsx new file mode 100644 index 0000000000..7e123a3d75 --- /dev/null +++ b/stories/bar/48_test_tooltip.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { getChartRotationKnob } from '../utils/knobs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +// for testing purposes only +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/4_ordinal.tsx b/stories/bar/4_ordinal.tsx new file mode 100644 index 0000000000..f162fdc87e --- /dev/null +++ b/stories/bar/4_ordinal.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/5_linear.tsx b/stories/bar/5_linear.tsx new file mode 100644 index 0000000000..9f6a87b8a5 --- /dev/null +++ b/stories/bar/5_linear.tsx @@ -0,0 +1,46 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, LIGHT_THEME, Position, ScaleType, Settings } from '../../src'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const theme = { + ...LIGHT_THEME, + scales: { + histogramPadding: number('histogram padding', 0, { + range: true, + min: 0, + max: 1, + step: 0.01, + }), + barsPadding: number('bar padding', 0, { + range: true, + min: 0, + max: 1, + step: 0.01, + }), + }, + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/6_linear_no_linear_interval.tsx b/stories/bar/6_linear_no_linear_interval.tsx new file mode 100644 index 0000000000..80b0faac09 --- /dev/null +++ b/stories/bar/6_linear_no_linear_interval.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => ( + + + + Number(d).toFixed(2)} /> + + + +); + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/7_with_time_xaxis.tsx b/stories/bar/7_with_time_xaxis.tsx new file mode 100644 index 0000000000..f962c3b0b8 --- /dev/null +++ b/stories/bar/7_with_time_xaxis.tsx @@ -0,0 +1,32 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, niceTimeFormatByDay, Position, ScaleType, Settings, timeFormatter } from '../../src'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +export const example = () => { + const formatter = timeFormatter(niceTimeFormatByDay(1)); + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/bar/8_with_log_yaxis.tsx b/stories/bar/8_with_log_yaxis.tsx new file mode 100644 index 0000000000..c88effc057 --- /dev/null +++ b/stories/bar/8_with_log_yaxis.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/9_with_stacked_log.tsx b/stories/bar/9_with_stacked_log.tsx new file mode 100644 index 0000000000..4fed1e5c1f --- /dev/null +++ b/stories/bar/9_with_stacked_log.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/bar/bars.stories.tsx b/stories/bar/bars.stories.tsx new file mode 100644 index 0000000000..93919185ca --- /dev/null +++ b/stories/bar/bars.stories.tsx @@ -0,0 +1,61 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Bar Chart', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as basic } from './1_basic'; +export { example as withValueLabel } from './2_label_value'; +export { example as withAxis } from './3_with_axis'; +export { example as withOrdinalXAxis } from './4_ordinal'; +export { example as withLinearXAxis } from './5_linear'; +export { example as withLinearXAxisNoLinearInterval } from './6_linear_no_linear_interval'; +export { example as withTimeXAxis } from './7_with_time_xaxis'; +export { example as withLogYAxis } from './8_with_log_yaxis'; +export { example as withStackedLogYAxis } from './9_with_stacked_log'; + +export { example as withAxisAndLegend } from './10_axis_and_legend'; +export { example as stackedWithAxisAndLegend } from './11_stacked_with_axis_and_legend'; +export { example as stackedAsPercentage } from './12_stacked_as_percentage'; +export { example as clusteredWithAxisAndLegend } from './13_clustered'; +export { example as clusteredMultipleSeriesSpecs } from './14_clustered_multiple'; +export { example as timeClusteredUsingVariousSpecs } from './15_time_clustered'; +export { example as timeStackedUsingVariousSpecs } from './17_time_stacked'; +export { example as barChart1y0g } from './18_bar_chart_1y0g'; +export { example as barChart1y1g } from './19_bar_chart_1y1g'; + +export { example as barChart1y2g } from './20_bar_chart_1y2g'; +export { example as barChart2y0g } from './21_bar_chart_2y0g'; +export { example as barChart2y1g } from './22_barchart_2y1g'; +export { example as barChart2y2g } from './23_bar_chart_2y2g'; +export { example as tooltipSeriesVisibility } from './24_tooltip_visibility'; +export { example as withHighDataVolume } from './25_high_data_volume'; +export { example as singleDataChartLinear } from './26_single_data_linear'; +export { example as singleDataChartOrdinal } from './27_single_data_ordinal'; +export { example as singleDataClusteredChart } from './28_single_data_clustered'; +export { example as singleDataStackedChart } from './29_single_data_stacked'; + +export { example as stackedToExtent } from './30_stacked_to_extent'; +export { example as negativeAndPositiveXValues } from './31_negative_and_positive_x_values'; +export { example as scaleToExtent } from './32_scale_to_extent'; +export { example as bandBarChart } from './33_band_bar'; +export { example as minHeight } from './45_min_height'; +export { example as stackedOnlyGroupedAreas } from './47_stacked_only_grouped'; + +// for testing purposes only +export { example as testLinear } from './34_test_linear'; +export { example as testTime } from './35_test_time'; +export { example as testLinearClustered } from './36_test_linear_clustered'; +export { example as testTimeClustered } from './37_test_time_clustered'; +export { example as testClusteredBarChartWithNullBars } from './38_test_clustered_null_bars'; +export { example as testStackedBarChartWithNullBars } from './39_test_stacked_null'; +export { example as testSwitchOrdinalLinearAxis } from './40_test_switch'; +export { example as testHistogramModeLinear } from './41_test_histogram_linear'; +export { example as testHistogramModeOrdinal } from './42_test_histogram_ordinal'; +export { example as testDiscover } from './43_test_discover'; +export { example as testSingleHistogramBarChart } from './44_test_single_histogram'; +export { example as testMinHeightPositiveAndNegativeValues } from './46_test_min_height'; +export { example as testTooltipAndRotation } from './48_test_tooltip'; diff --git a/stories/bar_chart.tsx b/stories/bar_chart.tsx deleted file mode 100644 index 95f8b74442..0000000000 --- a/stories/bar_chart.tsx +++ /dev/null @@ -1,1992 +0,0 @@ -import { boolean, color, number, select } from '@storybook/addon-knobs'; -import { DateTime } from 'luxon'; -import React from 'react'; - -import { - AnnotationDomainTypes, - AreaSeries, - Axis, - BarSeries, - Chart, - DARK_THEME, - HistogramBarSeries, - HistogramModeAlignments, - LIGHT_THEME, - LineAnnotation, - LineSeries, - niceTimeFormatByDay, - Position, - RectAnnotation, - ScaleType, - Settings, - timeFormatter, - TooltipType, -} from '../src'; -import { SeededDataGenerator, getRandomNumberGenerator } from '../src/mocks/utils'; -import * as TestDatasets from '../src/utils/data_samples/test_dataset'; -import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { TEST_DATASET_DISCOVER } from '../src/utils/data_samples/test_dataset_discover_per_30s'; -import { FilterPredicate } from '../src/chart_types/xy_chart/utils/specs'; -import { getChartRotationKnob } from './common'; - -const dateFormatter = timeFormatter('HH:mm:ss'); - -const dataGen = new SeededDataGenerator(); -function generateDataWithAdditional(num: number) { - return [...dataGen.generateSimpleSeries(num), { x: num, y: 0.25, g: 0 }, { x: num + 1, y: 8, g: 0 }]; -} -const frozenDataSmallVolume = generateDataWithAdditional(10); -const frozenDataMediumVolume = generateDataWithAdditional(50); -const frozenDataHighVolume = generateDataWithAdditional(1500); - -const frozenData: { [key: string]: any[] } = { - s: frozenDataSmallVolume, - m: frozenDataMediumVolume, - h: frozenDataHighVolume, -}; - -export default { - title: 'Bar Chart', - parameters: { - info: { - source: false, - }, - }, -}; - -export const basic = () => { - const darkmode = boolean('darkmode', false); - const className = darkmode ? 'story-chart-dark' : 'story-chart'; - const toggleSpec = boolean('toggle bar spec', true); - const data1 = [ - { x: 0, y: 2 }, - { x: 1, y: 7 }, - { x: 2, y: 3 }, - { x: 3, y: 6 }, - ]; - const data2 = data1.map((datum) => ({ ...datum, y: datum.y - 1 })); - const data = toggleSpec ? data1 : data2; - const specId = toggleSpec ? 'bars1' : 'bars2'; - return ( - - - - ); -}; -basic.story = { - name: 'basic', -}; - -export const withValueLabel = () => { - const showValueLabel = boolean('show value label', true); - const isAlternatingValueLabel = boolean('alternating value label', false); - const isValueContainedInElement = boolean('contain value label within bar element', false); - const hideClippedValue = boolean('hide clipped value', false); - - const displayValueSettings = { - showValueLabel, - isAlternatingValueLabel, - isValueContainedInElement, - hideClippedValue, - }; - - const debug = boolean('debug', false); - - const theme = { - barSeriesStyle: { - displayValue: { - fontSize: number('value font size', 10), - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, - fontStyle: 'normal', - padding: 0, - fill: color('value color', '#000'), - offsetX: number('offsetX', 0), - offsetY: number('offsetY', 0), - }, - }, - }; - - const dataSize = select( - 'data volume size', - { - 'small volume': 's', - 'medium volume': 'm', - 'high volume': 'h', - }, - 's', - ); - const data = frozenData[dataSize]; - - const isSplitSeries = boolean('split series', false); - const isStackedSeries = boolean('stacked series', false); - - const splitSeriesAccessors = isSplitSeries ? ['g'] : undefined; - const stackAccessors = isStackedSeries ? ['x'] : undefined; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -withValueLabel.story = { - name: 'with value label', -}; - -export const withAxis = () => { - const darkmode = boolean('darkmode', false); - const className = darkmode ? 'story-chart-dark' : 'story-chart'; - const defaultTheme = darkmode ? DARK_THEME : LIGHT_THEME; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -withAxis.story = { - name: 'with axis', -}; - -export const withOrdinalXAxis = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -withOrdinalXAxis.story = { - name: 'with ordinal x axis', -}; - -export const withLinearXAxis = () => { - const theme = { - ...LIGHT_THEME, - scales: { - histogramPadding: number('histogram padding', 0, { - range: true, - min: 0, - max: 1, - step: 0.01, - }), - barsPadding: number('bar padding', 0, { - range: true, - min: 0, - max: 1, - step: 0.01, - }), - }, - }; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -withLinearXAxis.story = { - name: 'with linear x axis', -}; - -export const withLinearXAxisNoLinearInterval = () => ( - - - - Number(d).toFixed(2)} /> - - - -); -withLinearXAxisNoLinearInterval.story = { - name: 'with linear x axis no linear interval', -}; - -export const withTimeXAxis = () => { - const formatter = timeFormatter(niceTimeFormatByDay(1)); - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -withTimeXAxis.story = { - name: 'with time x axis', -}; - -export const withLogYAxis = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -withLogYAxis.story = { - name: 'with log y axis', -}; - -export const withStackedLogYAxis = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -withStackedLogYAxis.story = { - name: 'with stacked log y axis', -}; - -export const withAxisAndLegend = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -withAxisAndLegend.story = { - name: 'with axis and legend', -}; - -export const stackedWithAxisAndLegend = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -stackedWithAxisAndLegend.story = { - name: 'stacked with axis and legend', -}; - -export const stackedAsPercentage = () => { - const stackedAsPercentage = boolean('stacked as percentage', true); - const clusterBars = boolean('cluster', true); - return ( - - - - (stackedAsPercentage && !clusterBars ? `${Number(d * 100).toFixed(0)} %` : d)} - /> - - - - ); -}; -stackedAsPercentage.story = { - name: 'stacked as percentage', -}; - -export const clusteredWithAxisAndLegend = () => { - const theme = { - ...LIGHT_THEME, - scales: { - histogramPadding: number('histogram padding', 0.05, { - range: true, - min: 0, - max: 1, - step: 0.01, - }), - barsPadding: number('bar padding', 0.25, { - range: true, - min: 0, - max: 1, - step: 0.01, - }), - }, - }; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -clusteredWithAxisAndLegend.story = { - name: 'clustered with axis and legend', -}; - -export const clusteredMultipleSeriesSpecs = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - - - ); -}; -clusteredMultipleSeriesSpecs.story = { - name: 'clustered multiple series specs', -}; - -export const timeClusteredUsingVariousSpecs = () => { - const formatter = timeFormatter(niceTimeFormatByDay(1)); - return ( - - - - Number(d).toFixed(2)} /> - - - - - - ); -}; -timeClusteredUsingVariousSpecs.story = { - name: 'time clustered using various specs', -}; - -export const timeStackedUsingVariousSpecs = () => { - const formatter = timeFormatter(niceTimeFormatByDay(1)); - return ( - - - - Number(d).toFixed(2)} /> - - - - - - ); -}; -timeStackedUsingVariousSpecs.story = { - name: 'time stacked using various specs', -}; - -export const barChart1y0g = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -barChart1y0g.story = { - name: '1y0g', -}; - -export const barChart1y1g = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -barChart1y1g.story = { - name: '1y1g', -}; - -export const barChart1y2g = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -barChart1y2g.story = { - name: '1y2g', -}; - -export const barChart2y0g = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -barChart2y0g.story = { - name: '2y0g', -}; - -export const barChart2y1g = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -barChart2y1g.story = { - name: '2y1g', -}; - -export const barChart2y2g = () => { - const isVisibleFunction: FilterPredicate = (series) => { - return series.splitAccessors.size > 0 - ? series.specId === 'bars1' && series.yAccessor === 'y1' && series.splitAccessors.get('g1') === 'cloudflare.com' - : series.specId === 'bars1' && series.yAccessor === 'y1'; - }; - - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -barChart2y2g.story = { - name: '2y2g', -}; - -export const tooltipSeriesVisibility = () => { - const isVisibleFunction: FilterPredicate = (series) => { - return series.splitAccessors.get('g1') === 'cloudflare.com'; - }; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -tooltipSeriesVisibility.story = { - name: 'tooltip series visibility', -}; - -export const withHighDataVolume = () => { - const dg = new SeededDataGenerator(); - const data = dg.generateSimpleSeries(15000); - const tooltipProps = { - type: TooltipType.Follow, - }; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -withHighDataVolume.story = { - name: 'with high data volume', - info: { - source: false, - }, -}; - -export const singleDataChartLinear = () => { - const hasCustomDomain = boolean('has custom domain', false); - const xDomain = hasCustomDomain - ? { - min: 0, - } - : undefined; - - const theme = { - scales: { - barsPadding: number('bars padding', 0.25, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - }, - }; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -singleDataChartLinear.story = { - name: 'single data chart [linear]', -}; - -export const singleDataChartOrdinal = () => { - const hasCustomDomain = boolean('has custom domain', false); - const xDomain = hasCustomDomain ? ['a', 'b'] : undefined; - - const theme = { - scales: { - barsPadding: number('bars padding', 0.25, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - }, - }; - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -singleDataChartOrdinal.story = { - name: 'single data chart [ordinal]', -}; - -export const singleDataClusteredChart = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -singleDataClusteredChart.story = { - name: 'single data clusterd chart', -}; - -export const singleDataStackedChart = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -singleDataStackedChart.story = { - name: 'single data stacked chart', -}; - -export const singldedatachartstackedtoextent = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -singldedatachartstackedtoextent.story = { - name: 'single data stacked chart scale to extent', -}; - -export const singleDataClusteredChartScaleToExtent = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -singleDataClusteredChartScaleToExtent.story = { - name: 'single data clustered chart scale to extent', -}; - -export const negativeAndPositiveXValues = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -negativeAndPositiveXValues.story = { - name: 'negative and positive x values', -}; - -export const scaleToExtent = () => { - const yScaleToDataExtent = boolean('yScaleDataToExtent', true); - const mixed = [ - { x: 0, y: -4 }, - { x: 1, y: -3 }, - { x: 2, y: 2 }, - { x: 3, y: 1 }, - ]; - - const allPositive = mixed.map((datum) => ({ x: datum.x, y: Math.abs(datum.y) })); - const allNegative = mixed.map((datum) => ({ x: datum.x, y: Math.abs(datum.y) * -1 })); - - const dataChoice = select( - 'data', - { - mixed: 'mixed', - allPositive: 'all positive', - allNegative: 'all negative', - }, - 'all negative', - ); - - let data = mixed; - switch (dataChoice) { - case 'all positive': - data = allPositive; - break; - case 'all negative': - data = allNegative; - break; - } - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -scaleToExtent.story = { - name: 'scale to extent', -}; - -export const bandBarChart = () => { - const getRandomNumber = getRandomNumberGenerator(); - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d: any) => { - return { - x: d[0], - max: d[1] + 4 + 4 * getRandomNumber(), - min: d[1] - 4 - 4 * getRandomNumber(), - }; - }); - const lineData = KIBANA_METRICS.metrics.kibana_os_load[0].data.map((d: any) => { - return [d[0], d[1]]; - }); - const scaleToDataExtent = boolean('scale to extent', true); - return ( - - - Number(d).toFixed(2)} - /> - - - - - - ); -}; -bandBarChart.story = { - name: 'band bar chart', -}; - -export const testLinear = () => { - const data = [ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 4], - [7, 3], - [8, 2], - [9, 1], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testLinear.story = { - name: '[test] - linear', -}; - -export const testTime = () => { - const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); - const data = [ - [start.toMillis(), 1], - [start.plus({ minute: 1 }).toMillis(), 2], - [start.plus({ minute: 2 }).toMillis(), 3], - [start.plus({ minute: 3 }).toMillis(), 4], - [start.plus({ minute: 4 }).toMillis(), 5], - [start.plus({ minute: 5 }).toMillis(), 4], - [start.plus({ minute: 6 }).toMillis(), 3], - [start.plus({ minute: 7 }).toMillis(), 2], - [start.plus({ minute: 8 }).toMillis(), 1], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testTime.story = { - name: '[test] - time', -}; - -export const testLinearClustered = () => { - const data9 = [ - [1, 1, 3], - [2, 2, 4], - [3, 3, 5], - [4, 4, 6], - [5, 5, 7], - [6, 4, 6], - [7, 3, 5], - [8, 2, 4], - [9, 1, 3], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testLinearClustered.story = { - name: '[test] - linear clustered', -}; - -export const testTimeClustered = () => { - const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); - const data = [ - [start.toMillis(), 1, 4], - [start.plus({ minute: 1 }).toMillis(), 2, 5], - [start.plus({ minute: 2 }).toMillis(), 3, 6], - [start.plus({ minute: 3 }).toMillis(), 4, 7], - [start.plus({ minute: 4 }).toMillis(), 5, 8], - [start.plus({ minute: 5 }).toMillis(), 4, 7], - [start.plus({ minute: 6 }).toMillis(), 3, 6], - [start.plus({ minute: 7 }).toMillis(), 2, 5], - [start.plus({ minute: 8 }).toMillis(), 1, 4], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testTimeClustered.story = { - name: '[test] - time clustered', -}; - -export const testClusteredBarChartWithNullBars = () => { - const data = [ - [1, 1, 3, 'a'], - [2, null, 4, 'a'], - [3, 3, 5, 'a'], - [4, 4, 6, 'a'], - [1, 1, 3, 'b'], - [2, 2, 4, 'b'], - [3, 3, 5, 'b'], - [4, 4, 6, 'b'], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testClusteredBarChartWithNullBars.story = { - name: '[test] - clustered bar chart with null bars', -}; - -export const testStackedBarChartWithNullBars = () => { - const data = [ - [1, 1, 3, 'a'], - [2, null, 4, 'a'], - [3, 3, 5, 'a'], - [4, 4, 6, 'a'], - [1, 1, 3, 'b'], - [2, 2, 4, 'b'], - [3, null, 5, 'b'], - [4, 4, 6, 'b'], - ]; - return ( - - - Number(d).toFixed(2)} - /> - - - ); -}; -testStackedBarChartWithNullBars.story = { - name: '[test] - stacked bar chart with null bars', -}; - -export const testSwitchOrdinalLinearAxis = () => { - return ( - - - Number(d).toFixed(2)} /> - - - - ); -}; -testSwitchOrdinalLinearAxis.story = { - name: '[test] switch ordinal/linear x axis', -}; - -export const testHistogramModeLinear = () => { - const data = TestDatasets.BARCHART_2Y1G; - - const lineAnnotationStyle = { - line: { - strokeWidth: 2, - stroke: '#c80000', - opacity: 0.3, - }, - }; - - const theme = { - scales: { - barsPadding: number('bars padding', 0.25, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - histogramPadding: number('histogram padding', 0.05, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - }, - }; - - const otherSeriesSelection = select( - 'other series', - { - line: 'line', - area: 'area', - }, - 'line', - ); - - const pointAlignment = select('point series alignment', HistogramModeAlignments, HistogramModeAlignments.Center); - const pointData = TestDatasets.BARCHART_1Y0G; - - const otherSeries = - otherSeriesSelection === 'line' ? ( - - ) : ( - - ); - - const hasHistogramBarSeries = boolean('hasHistogramBarSeries', false); - return ( - - - } - /> - - - - {hasHistogramBarSeries && ( - - )} - - - {otherSeries} - - ); -}; -testHistogramModeLinear.story = { - name: '[test] histogram mode (linear)', -}; - -export const testHistogramModeOrdinal = () => { - const data = [ - { x: 'a', y: 2 }, - { x: 'b', y: 7 }, - { x: 'c', y: 0 }, - { x: 'd', y: 6 }, - ]; - const theme = { - scales: { - barsPadding: number('bars padding', 0.25, { - range: true, - min: 0, - max: 1, - step: 0.1, - }), - }, - }; - - const hasHistogramBarSeries = boolean('hasHistogramBarSeries', false); - return ( - - - - - {hasHistogramBarSeries && ( - - )} - - - - ); -}; -testHistogramModeOrdinal.story = { - name: '[test] histogram mode (ordinal)', -}; - -export const testDiscover = () => { - const data = TEST_DATASET_DISCOVER.series[0].values; - - const formatter = timeFormatter(niceTimeFormatByDay(1)); - - const xDomain = { - minInterval: 30000, - }; - - const useCustomMinInterval = boolean('use custom minInterval of 30s', true); - return ( - - - - - - - - ); -}; -testDiscover.story = { - name: '[test] discover', -}; - -export const testSingleHistogramBarChart = () => { - const formatter = timeFormatter(niceTimeFormatByDay(1)); - - const xDomain = { - minInterval: 60000, - }; - - return ( - - - - - - - ); -}; -testSingleHistogramBarChart.story = { - name: '[test] single histogram bar chart', -}; - -export const MinHeight = () => { - const minBarHeight = number('minBarHeight', 5); - const data = [ - [1, 100000], - [2, 10000], - [3, 1000], - [4, 100], - [5, 10], - [6, 1], - [7, 0], - [8, 1], - [9, 0], - ]; - return ( - - - - - - ); -}; -MinHeight.story = { - name: 'Min Height', -}; - -export const testMinHeightPositiveAndNegativeValues = () => { - const minBarHeight = number('minBarHeight', 10); - const data = [ - [1, -100000], - [2, -10000], - [3, -1000], - [4, -100], - [5, -10], - [6, -1], - [7, 0], - [8, -1], - [9, 0], - [10, 0], - [11, 1], - [12, 0], - [13, 1], - [14, 10], - [15, 100], - [16, 1000], - [17, 10000], - [18, 100000], - ]; - return ( - - - - - - ); -}; -testMinHeightPositiveAndNegativeValues.story = { - name: '[Test] Min Height - positive and negative values', -}; - -export const stackedOnlyGroupedAreas = () => { - const data1 = [ - [1, 2], - [2, 2], - [3, 3], - [4, 5], - [5, 5], - [6, 3], - [7, 8], - [8, 2], - [9, 1], - ]; - const data2 = [ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 4], - [7, 3], - [8, 2], - [9, 4], - ]; - const data3 = [ - [1, 6], - [2, 6], - [3, 3], - [4, 2], - [5, 1], - [6, 1], - [7, 5], - [8, 6], - [9, 7], - ]; - const data4 = [ - [1, 2], - [2, 6], - [3, 2], - [4, 9], - [5, 2], - [6, 3], - [7, 1], - [8, 2], - [9, 7], - ]; - const data5 = [ - [1, 1], - [2, 7], - [3, 5], - [4, 6], - [5, 5], - [6, 4], - [7, 2], - [8, 4], - [9, 8], - ]; - return ( - - - Number(d).toFixed(2)} - domain={{ min: 0, max: 15 }} - /> - Number(d).toFixed(2)} - hide={true} - domain={{ min: 0, max: 15 }} - /> - - - - - - - - ); -}; -stackedOnlyGroupedAreas.story = { - name: 'stacked only grouped areas', -}; - -export const testTooltipAndRotation = () => { - return ( - - - - Number(d).toFixed(2)} /> - - - - ); -}; -testTooltipAndRotation.story = { - name: '[test] tooltip and rotation', -}; diff --git a/stories/grid.tsx b/stories/grids/1_basic.tsx similarity index 62% rename from stories/grid.tsx rename to stories/grids/1_basic.tsx index 15ef64ac21..0934b5e84e 100644 --- a/stories/grid.tsx +++ b/stories/grids/1_basic.tsx @@ -1,18 +1,6 @@ import { boolean, color, number } from '@storybook/addon-knobs'; import React from 'react'; -import { - Axis, - BarSeries, - Chart, - getAxisId, - getGroupId, - getSpecId, - GridLineConfig, - LineSeries, - Position, - ScaleType, - Settings, -} from '../src/'; +import { Axis, BarSeries, Chart, GridLineConfig, LineSeries, Position, ScaleType, Settings } from '../../src'; function generateGridLineConfig(group: string, gridColor = 'purple'): GridLineConfig { const groupId = `${group} axis`; @@ -68,16 +56,7 @@ function generateGridLineConfig(group: string, gridColor = 'purple'): GridLineCo }; } -export default { - title: 'Grids', - parameters: { - info: { - source: false, - }, - }, -}; - -export const basic = () => { +export const example = () => { const leftAxisGridLineConfig = generateGridLineConfig(Position.Left, 'lightblue'); const rightAxisGridLineConfig = generateGridLineConfig(Position.Right, 'red'); const topAxisGridLineConfig = generateGridLineConfig(Position.Top, 'teal'); @@ -94,38 +73,38 @@ export const basic = () => { const integersOnlyLeft = boolean('left axis show only integer values', false, 'left axis'); const integersOnlyRight = boolean('right axis show only intger values', false, 'right axis'); return ( - + Number(d).toFixed(0) : (d) => Number(d).toFixed(2)} showGridLines={boolean('show left axis grid lines', false, 'left axis')} gridLineStyle={toggleHorizontalAxisGridLineStyle ? leftAxisGridLineConfig : undefined} integersOnly={integersOnlyLeft} /> Number(d).toFixed(0) : (d) => Number(d).toFixed(2)} showGridLines={boolean('show right axis grid lines', false, 'right axis')} @@ -133,67 +112,7 @@ export const basic = () => { integersOnly={integersOnlyRight} /> - - - ); -}; -basic.story = { - name: 'basic', -}; - -export const multipleAxesWithTheSamePosition = () => { - const leftAxisGridLineConfig = generateGridLineConfig(Position.Left); - const leftAxisGridLineConfig2 = generateGridLineConfig(`${Position.Left}2`); - - return ( - - - Number(d).toFixed(2)} - showGridLines={boolean('show left axis grid lines', false, 'left axis')} - gridLineStyle={leftAxisGridLineConfig} - /> - Number(d).toFixed(2)} - showGridLines={boolean('show left axis 2 grid lines', false, 'left2 axis')} - gridLineStyle={leftAxisGridLineConfig2} - /> - { ]} /> { ); }; -multipleAxesWithTheSamePosition.story = { - name: 'multiple axes with the same position', -}; diff --git a/stories/grids/2_multiple_axes.tsx b/stories/grids/2_multiple_axes.tsx new file mode 100644 index 0000000000..2375cfa79f --- /dev/null +++ b/stories/grids/2_multiple_axes.tsx @@ -0,0 +1,114 @@ +import { boolean, color, number } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, GridLineConfig, LineSeries, Position, ScaleType, Settings } from '../../src'; + +function generateGridLineConfig(group: string, gridColor = 'purple'): GridLineConfig { + const groupId = `${group} axis`; + + return { + stroke: color(`${groupId} grid line stroke color`, gridColor, groupId), + strokeWidth: number( + `${groupId} grid line stroke width`, + 1, + { + range: true, + min: 0, + max: 10, + step: 1, + }, + groupId, + ), + opacity: number( + `${groupId} grid line stroke opacity`, + 1, + { + range: true, + min: 0, + max: 1, + step: 0.01, + }, + groupId, + ), + dash: [ + number( + `${groupId} grid line dash length`, + 1, + { + range: true, + min: 0, + max: 10, + step: 1, + }, + groupId, + ), + number( + `${groupId} grid line dash spacing`, + 1, + { + range: true, + min: 0, + max: 10, + step: 1, + }, + groupId, + ), + ], + }; +} + +export const example = () => { + const leftAxisGridLineConfig = generateGridLineConfig(Position.Left); + const leftAxisGridLineConfig2 = generateGridLineConfig(`${Position.Left}2`); + + return ( + + + Number(d).toFixed(2)} + showGridLines={boolean('show left axis grid lines', false, 'left axis')} + gridLineStyle={leftAxisGridLineConfig} + /> + Number(d).toFixed(2)} + showGridLines={boolean('show left axis 2 grid lines', false, 'left2 axis')} + gridLineStyle={leftAxisGridLineConfig2} + /> + + + + ); +}; diff --git a/stories/grids/grids.stories.tsx b/stories/grids/grids.stories.tsx new file mode 100644 index 0000000000..1b5c2a0c19 --- /dev/null +++ b/stories/grids/grids.stories.tsx @@ -0,0 +1,11 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Grids', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as basic } from './1_basic'; +export { example as multipleAxesWithTheSamePosition } from './2_multiple_axes'; diff --git a/stories/interactions.tsx b/stories/interactions.tsx deleted file mode 100644 index ace87a346e..0000000000 --- a/stories/interactions.tsx +++ /dev/null @@ -1,933 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import React from 'react'; -import { - AreaSeries, - Axis, - BarSeries, - Chart, - CurveType, - DARK_THEME, - getAxisId, - getSpecId, - LIGHT_THEME, - LineSeries, - niceTimeFormatByDay, - niceTimeFormatter, - Position, - ScaleType, - Settings, - timeFormatter, - TooltipType, - TooltipValue, - TooltipValueFormatter, - HistogramBarSeries, -} from '../src/'; - -import { array, boolean, number, select, button } from '@storybook/addon-knobs'; -import { DateTime } from 'luxon'; -import { switchTheme } from '../.storybook/theme_service'; -import { BARCHART_2Y2G } from '../src/utils/data_samples/test_dataset'; -import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { getChartRotationKnob } from './common'; - -const onElementListeners = { - onElementClick: action('onElementClick'), - onElementOver: action('onElementOver'), - onElementOut: action('onElementOut'), -}; - -const onLegendItemListeners = { - onLegendItemOver: action('onLegendItemOver'), - onLegendItemOut: action('onLegendItemOut'), - onLegendItemClick: action('onLegendItemClick'), - onLegendItemPlusClick: action('onLegendItemPlusClick'), - onLegendItemMinusClick: action('onLegendItemMinusClick'), -}; - -const onRenderChange = action('onRenderChange'); -const onPointerUpdate = action('onPointerUpdate'); - -export default { - title: 'Interactions', - parameters: { - info: { - source: false, - }, - }, -}; - -export const barClicksAndHovers = () => { - const headerFormatter: TooltipValueFormatter = (tooltip: TooltipValue) => { - if (tooltip.value % 2 === 0) { - return ( -
-

special header for even x values

-

{tooltip.value}

-
- ); - } - - return tooltip.value; - }; - - const tooltipProps = { - headerFormatter, - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -barClicksAndHovers.story = { - name: 'bar clicks and hovers', -}; - -export const areaPointClicksAndHovers = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -areaPointClicksAndHovers.story = { - name: 'area point clicks and hovers', -}; - -export const linePointClicksAndHovers = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -linePointClicksAndHovers.story = { - name: 'line point clicks and hovers', -}; - -export const lineAreaBarPointClicksAndHovers = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - - ); -}; -lineAreaBarPointClicksAndHovers.story = { - name: 'line area bar point clicks and hovers', -}; - -export const clicksHoversOnLegendItemsBarChart = () => { - const notSpecChange = 'not spec change'; - const specChange = 'spec change'; - - const xDomain = { - min: number('xDomain min', 0, {}, notSpecChange), - max: number('xDomain max', 6, {}, notSpecChange), - }; - - const yDomain = { - min: number('yDomain min', 0, {}, notSpecChange), - max: number('yDomain max', 10, {}, notSpecChange), - }; - - const yScaleTypeOptions: { [key: string]: typeof ScaleType.Linear | typeof ScaleType.Log } = { - linear: ScaleType.Linear, - log: ScaleType.Log, - }; - const yScaleType = select('yScaleType', yScaleTypeOptions, ScaleType.Linear, specChange); - - const xAccessorOptions = { x: 'x', y1: 'y1', y2: 'y2' }; - const xAccessor = select('xAccessor', xAccessorOptions, 'x', notSpecChange); - - const yScaleToDataExtent = boolean('yScaleDataToExtent', false, specChange); - - const splitSeriesAccessors = array('split series accessors', ['g1', 'g2'], ',', specChange); - - const hasY2 = boolean('has y2 yAccessor', true, specChange); - const yAccessors = hasY2 ? ['y1', 'y2'] : ['y1']; - - const additionalG1Value = { x: 4, g1: '$$$$$$$$', g2: 'indirect-cdn', y1: 7, y2: 3 }; - const hasAdditionalG1Value = boolean('has additional g1 value', false, specChange); - - const seriesData = BARCHART_2Y2G; - - const data = hasAdditionalG1Value ? [...seriesData, additionalG1Value] : seriesData; - - return ( - - - - Number(d).toFixed(2)} - domain={yDomain} - /> - - - - ); -}; -clicksHoversOnLegendItemsBarChart.story = { - name: 'click/hovers on legend items [bar chart]', -}; - -export const clickHoversOnLegendItemsAreaChart = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -clickHoversOnLegendItemsAreaChart.story = { - name: 'click/hovers on legend items [area chart]', -}; - -export const clickHoversOnLegendItemsLineChart = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - - - - - ); -}; -clickHoversOnLegendItemsLineChart.story = { - name: 'click/hovers on legend items [line chart]', -}; - -export const clickHoversOnLegendItemsMixedChart = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -clickHoversOnLegendItemsMixedChart.story = { - name: 'click/hovers on legend items [mixed chart]', -}; - -export const brushSelectionToolOnLinear = () => { - return ( - - - - Number(d).toFixed(2)} /> - - Number(d).toFixed(2)} - /> - - - - ); -}; -brushSelectionToolOnLinear.story = { - name: 'brush selection tool on linear', -}; - -export const brushSelectionToolOnBarChartLinear = () => { - return ( - - - - Number(d).toFixed(2)} /> - - Number(d).toFixed(2)} - /> - - - - ); -}; -brushSelectionToolOnBarChartLinear.story = { - name: 'brush selection tool on bar chart linear', -}; - -export const brushSelectionToolOnTimeCharts = () => { - const now = DateTime.fromISO('2019-01-11T00:00:00.000') - .setZone('utc+1') - .toMillis(); - const oneDay = 1000 * 60 * 60 * 24; - const formatter = niceTimeFormatter([now, now + oneDay * 5]); - return ( - - { - action('onBrushEnd')(formatter(start), formatter(end)); - }} - onElementClick={action('onElementClick')} - rotation={getChartRotationKnob()} - /> - - Number(d).toFixed(2)} /> - - - - - ); -}; -brushSelectionToolOnTimeCharts.story = { - name: 'brush selection tool on time charts', -}; - -export const brushSelectionToolOnHistogramTimeCharts = () => { - const now = DateTime.fromISO('2019-01-11T00:00:00.000') - .setZone('utc+1') - .toMillis(); - const oneDay = 1000 * 60 * 60 * 24; - const formatter = niceTimeFormatter([now, now + oneDay * 5]); - return ( - - { - action('onBrushEnd')(formatter(start), formatter(end)); - }} - onElementClick={action('onElementClick')} - rotation={getChartRotationKnob()} - /> - - Number(d).toFixed(2)} /> - - - - ); -}; -brushSelectionToolOnHistogramTimeCharts.story = { - name: 'brush selection tool on histogram time charts', -}; - -export const brushDisabledOnOrdinalXAxis = () => { - return ( - - - - - - - ); -}; -brushDisabledOnOrdinalXAxis.story = { - name: 'brush disabled on ordinal x axis', -}; - -export const crosshairWithTimeAxis = () => { - const hideBars = boolean('hideBars', false); - const formatter = timeFormatter(niceTimeFormatByDay(1)); - const darkmode = boolean('darkmode', false); - const className = darkmode ? 'story-chart-dark' : 'story-chart'; - const defaultTheme = darkmode ? DARK_THEME : LIGHT_THEME; - switchTheme(darkmode ? 'dark' : 'light'); - const chartRotation = getChartRotationKnob(); - const numberFormatter = (d: any) => Number(d).toFixed(2); - - const tooltipType = select( - 'tooltipType', - { - cross: TooltipType.Crosshairs, - vertical: TooltipType.VerticalCursor, - follow: TooltipType.Follow, - none: TooltipType.None, - }, - TooltipType.Crosshairs, - ); - - const tooltipProps = { - type: tooltipType, - snap: boolean('tooltip snap to grid', true), - }; - - return ( - - - - - {!hideBars && ( - - )} - {!hideBars && ( - - )} - - - ); -}; -crosshairWithTimeAxis.story = { - name: 'crosshair with time axis', -}; - -export const renderChangeAction = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -renderChangeAction.story = { - name: 'Render change action', -}; -// info: { -// text: -// 'Sends an event every time the chart render state changes. This is provided to bind attributes to the chart for visulaization loading checks.', -// }, - -export const cursorUpdateAction = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -cursorUpdateAction.story = { - name: 'Cursor update action', - // info: { - // text: 'Sends an event every time the cursor changes. This is provided to sync cursors between multiple charts.', - // }, -}; - -export const pngExportAction = () => { - /** - * The handler section of this story demonstrates the PNG export functionality - */ - const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 100); - const label = 'Export PNG'; - const chartRef: React.RefObject = React.createRef(); - const handler = () => { - if (!chartRef.current) { - return; - } - const snapshot = chartRef.current.getPNGSnapshot({ - // you can set the background and pixel ratio for the PNG export - backgroundColor: 'white', - pixelRatio: 2, - }); - if (!snapshot) { - return; - } - // will save as chart.png - const fileName = 'chart.png'; - switch (snapshot.browser) { - case 'IE11': - return navigator.msSaveBlob(snapshot.blobOrDataUrl, fileName); - default: - const link = document.createElement('a'); - link.download = fileName; - link.href = snapshot.blobOrDataUrl; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - } - }; - const groupId = ''; - button(label, handler, groupId); - return ( - - - - - - - - ); -}; -pngExportAction.story = { - name: 'PNG export action', -}; -// { -// info: { -// text: -// 'Generate a PNG of the chart by clicking on the Export PNG button in the knobs section. In this example, the button handler is setting the PNG background to white with a pixel ratio of 2. If the browser is detected to be IE11, msSaveBlob will be used instead of a PNG capture.', -// }, -// }, -// ); diff --git a/stories/interactions/10_brush_selection_bar.tsx b/stories/interactions/10_brush_selection_bar.tsx new file mode 100644 index 0000000000..3395aebb06 --- /dev/null +++ b/stories/interactions/10_brush_selection_bar.tsx @@ -0,0 +1,30 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/11_brush_time.tsx b/stories/interactions/11_brush_time.tsx new file mode 100644 index 0000000000..6a522af26e --- /dev/null +++ b/stories/interactions/11_brush_time.tsx @@ -0,0 +1,58 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, LineSeries, niceTimeFormatter, Position, ScaleType, Settings } from '../../src'; + +import { boolean } from '@storybook/addon-knobs'; +import { DateTime } from 'luxon'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const now = DateTime.fromISO('2019-01-11T00:00:00.000') + .setZone('utc+1') + .toMillis(); + const oneDay = 1000 * 60 * 60 * 24; + const formatter = niceTimeFormatter([now, now + oneDay * 5]); + return ( + + { + action('onBrushEnd')(formatter(start), formatter(end)); + }} + onElementClick={action('onElementClick')} + rotation={getChartRotationKnob()} + /> + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/interactions/12_brush_time_hist.tsx b/stories/interactions/12_brush_time_hist.tsx new file mode 100644 index 0000000000..2042afddb1 --- /dev/null +++ b/stories/interactions/12_brush_time_hist.tsx @@ -0,0 +1,44 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, Chart, niceTimeFormatter, Position, ScaleType, Settings, HistogramBarSeries } from '../../src'; + +import { boolean } from '@storybook/addon-knobs'; +import { DateTime } from 'luxon'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const now = DateTime.fromISO('2019-01-11T00:00:00.000') + .setZone('utc+1') + .toMillis(); + const oneDay = 1000 * 60 * 60 * 24; + const formatter = niceTimeFormatter([now, now + oneDay * 5]); + return ( + + { + action('onBrushEnd')(formatter(start), formatter(end)); + }} + onElementClick={action('onElementClick')} + rotation={getChartRotationKnob()} + /> + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/13_brush_disabled_ordinal.tsx b/stories/interactions/13_brush_disabled_ordinal.tsx new file mode 100644 index 0000000000..50d4e8218d --- /dev/null +++ b/stories/interactions/13_brush_disabled_ordinal.tsx @@ -0,0 +1,28 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + return ( + + + + + + + ); +}; diff --git a/stories/interactions/14_crosshair_time.tsx b/stories/interactions/14_crosshair_time.tsx new file mode 100644 index 0000000000..b8337bc4b3 --- /dev/null +++ b/stories/interactions/14_crosshair_time.tsx @@ -0,0 +1,105 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +import React from 'react'; +import { + Axis, + BarSeries, + Chart, + DARK_THEME, + LIGHT_THEME, + LineSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, + TooltipType, +} from '../../src/'; + +import { boolean, select } from '@storybook/addon-knobs'; +import { switchTheme } from '../../.storybook/theme_service'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + const hideBars = boolean('hideBars', false); + const formatter = timeFormatter(niceTimeFormatByDay(1)); + const darkmode = boolean('darkmode', false); + const className = darkmode ? 'story-chart-dark' : 'story-chart'; + const defaultTheme = darkmode ? DARK_THEME : LIGHT_THEME; + switchTheme(darkmode ? 'dark' : 'light'); + const chartRotation = getChartRotationKnob(); + const numberFormatter = (d: any) => Number(d).toFixed(2); + + const tooltipType = select( + 'tooltipType', + { + cross: TooltipType.Crosshairs, + vertical: TooltipType.VerticalCursor, + follow: TooltipType.Follow, + none: TooltipType.None, + }, + TooltipType.Crosshairs, + ); + + const tooltipProps = { + type: tooltipType, + snap: boolean('tooltip snap to grid', true), + }; + + return ( + + + + + {!hideBars && ( + + )} + {!hideBars && ( + + )} + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; diff --git a/stories/interactions/15_render_change.tsx b/stories/interactions/15_render_change.tsx new file mode 100644 index 0000000000..4dc528849d --- /dev/null +++ b/stories/interactions/15_render_change.tsx @@ -0,0 +1,37 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +const onRenderChange = action('onRenderChange'); + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +example.story = { + parameters: { + info: { + text: `Sends an event every time the chart render state changes. This is provided to bind attributes to the chart for visulaization loading checks.`, + }, + }, +}; diff --git a/stories/interactions/16_cursor_update_action.tsx b/stories/interactions/16_cursor_update_action.tsx new file mode 100644 index 0000000000..aa0d5916be --- /dev/null +++ b/stories/interactions/16_cursor_update_action.tsx @@ -0,0 +1,36 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +const onPointerUpdate = action('onPointerUpdate'); + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; +example.story = { + parameters: { + info: { + text: `Sends an event every time the cursor changes. This is provided to sync cursors between multiple charts.`, + }, + }, +}; diff --git a/stories/interactions/17_png_export.tsx b/stories/interactions/17_png_export.tsx new file mode 100644 index 0000000000..33c2bff6fd --- /dev/null +++ b/stories/interactions/17_png_export.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, niceTimeFormatter, Position, ScaleType, Settings } from '../../src/'; + +import { button } from '@storybook/addon-knobs'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export const example = () => { + /** + * The handler section of this story demonstrates the PNG export functionality + */ + const data = KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 100); + const label = 'Export PNG'; + const chartRef: React.RefObject = React.createRef(); + const handler = () => { + if (!chartRef.current) { + return; + } + const snapshot = chartRef.current.getPNGSnapshot({ + // you can set the background and pixel ratio for the PNG export + backgroundColor: 'white', + pixelRatio: 2, + }); + if (!snapshot) { + return; + } + // will save as chart.png + const fileName = 'chart.png'; + switch (snapshot.browser) { + case 'IE11': + return navigator.msSaveBlob(snapshot.blobOrDataUrl, fileName); + default: + const link = document.createElement('a'); + link.download = fileName; + link.href = snapshot.blobOrDataUrl; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + }; + const groupId = ''; + button(label, handler, groupId); + return ( + + + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + info: { + text: `Generate a PNG of the chart by clicking on the Export PNG button in the knobs section. In this example, the button handler is setting the PNG background to white with a pixel ratio of 2. If the browser is detected to be IE11, msSaveBlob will be used instead of a PNG capture.`, + }, + }, +}; diff --git a/stories/interactions/1_bar_clicks.tsx b/stories/interactions/1_bar_clicks.tsx new file mode 100644 index 0000000000..dad32cbc8f --- /dev/null +++ b/stories/interactions/1_bar_clicks.tsx @@ -0,0 +1,56 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, TooltipValue, TooltipValueFormatter } from '../../src/'; + +const onElementListeners = { + onElementClick: action('onElementClick'), + onElementOver: action('onElementOver'), + onElementOut: action('onElementOut'), +}; + +export const example = () => { + const headerFormatter: TooltipValueFormatter = (tooltip: TooltipValue) => { + if (tooltip.value % 2 === 0) { + return ( +
+

special header for even x values

+

{tooltip.value}

+
+ ); + } + + return tooltip.value; + }; + + const tooltipProps = { + headerFormatter, + }; + + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/2_area_point_clicks.tsx b/stories/interactions/2_area_point_clicks.tsx new file mode 100644 index 0000000000..35bec85b62 --- /dev/null +++ b/stories/interactions/2_area_point_clicks.tsx @@ -0,0 +1,33 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings } from '../../src/'; + +const onElementListeners = { + onElementClick: action('onElementClick'), + onElementOver: action('onElementOver'), + onElementOut: action('onElementOut'), +}; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/3_line_point_clicks.tsx b/stories/interactions/3_line_point_clicks.tsx new file mode 100644 index 0000000000..966986fcdd --- /dev/null +++ b/stories/interactions/3_line_point_clicks.tsx @@ -0,0 +1,33 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +const onElementListeners = { + onElementClick: action('onElementClick'), + onElementOver: action('onElementOver'), + onElementOut: action('onElementOut'), +}; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/4_line_area_bar_clicks.tsx b/stories/interactions/4_line_area_bar_clicks.tsx new file mode 100644 index 0000000000..f607a0c5a3 --- /dev/null +++ b/stories/interactions/4_line_area_bar_clicks.tsx @@ -0,0 +1,60 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { AreaSeries, Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +const onElementListeners = { + onElementClick: action('onElementClick'), + onElementOver: action('onElementOver'), + onElementOut: action('onElementOut'), +}; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + + + ); +}; diff --git a/stories/interactions/5_clicks_legend_items_bar.tsx b/stories/interactions/5_clicks_legend_items_bar.tsx new file mode 100644 index 0000000000..8021988fce --- /dev/null +++ b/stories/interactions/5_clicks_legend_items_bar.tsx @@ -0,0 +1,83 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +import { array, boolean, number, select } from '@storybook/addon-knobs'; +import { BARCHART_2Y2G } from '../../src/utils/data_samples/test_dataset'; + +const onLegendItemListeners = { + onLegendItemOver: action('onLegendItemOver'), + onLegendItemOut: action('onLegendItemOut'), + onLegendItemClick: action('onLegendItemClick'), + onLegendItemPlusClick: action('onLegendItemPlusClick'), + onLegendItemMinusClick: action('onLegendItemMinusClick'), +}; + +export const example = () => { + const notSpecChange = 'not spec change'; + const specChange = 'spec change'; + + const xDomain = { + min: number('xDomain min', 0, {}, notSpecChange), + max: number('xDomain max', 6, {}, notSpecChange), + }; + + const yDomain = { + min: number('yDomain min', 0, {}, notSpecChange), + max: number('yDomain max', 10, {}, notSpecChange), + }; + + const yScaleTypeOptions: { [key: string]: typeof ScaleType.Linear | typeof ScaleType.Log } = { + linear: ScaleType.Linear, + log: ScaleType.Log, + }; + const yScaleType = select('yScaleType', yScaleTypeOptions, ScaleType.Linear, specChange); + + const xAccessorOptions = { x: 'x', y1: 'y1', y2: 'y2' }; + const xAccessor = select('xAccessor', xAccessorOptions, 'x', notSpecChange); + + const yScaleToDataExtent = boolean('yScaleDataToExtent', false, specChange); + + const splitSeriesAccessors = array('split series accessors', ['g1', 'g2'], ',', specChange); + + const hasY2 = boolean('has y2 yAccessor', true, specChange); + const yAccessors = hasY2 ? ['y1', 'y2'] : ['y1']; + + const additionalG1Value = { x: 4, g1: '$$$$$$$$', g2: 'indirect-cdn', y1: 7, y2: 3 }; + const hasAdditionalG1Value = boolean('has additional g1 value', false, specChange); + + const seriesData = BARCHART_2Y2G; + + const data = hasAdditionalG1Value ? [...seriesData, additionalG1Value] : seriesData; + + return ( + + + + Number(d).toFixed(2)} + domain={yDomain} + /> + + + + ); +}; diff --git a/stories/interactions/6_clicks_legend_items_area.tsx b/stories/interactions/6_clicks_legend_items_area.tsx new file mode 100644 index 0000000000..11a8bf5691 --- /dev/null +++ b/stories/interactions/6_clicks_legend_items_area.tsx @@ -0,0 +1,40 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/7_clicks_legend_items_line.tsx b/stories/interactions/7_clicks_legend_items_line.tsx new file mode 100644 index 0000000000..31210b7a37 --- /dev/null +++ b/stories/interactions/7_clicks_legend_items_line.tsx @@ -0,0 +1,105 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, Chart, CurveType, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + + + + + + ); +}; diff --git a/stories/interactions/8_clicks_legend_items_mixed.tsx b/stories/interactions/8_clicks_legend_items_mixed.tsx new file mode 100644 index 0000000000..698acb6a42 --- /dev/null +++ b/stories/interactions/8_clicks_legend_items_mixed.tsx @@ -0,0 +1,49 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/interactions/9_brush_selection_linear.tsx b/stories/interactions/9_brush_selection_linear.tsx new file mode 100644 index 0000000000..824c8968ed --- /dev/null +++ b/stories/interactions/9_brush_selection_linear.tsx @@ -0,0 +1,31 @@ +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings } from '../../src/'; + +import { getChartRotationKnob } from '../utils/knobs'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/interactions/interactions.stories.tsx b/stories/interactions/interactions.stories.tsx new file mode 100644 index 0000000000..4f32edd221 --- /dev/null +++ b/stories/interactions/interactions.stories.tsx @@ -0,0 +1,27 @@ +import { SB_ACTION_PANEL } from '../utils/storybook'; + +export default { + title: 'Interactions', + parameters: { + options: { selectedPanel: SB_ACTION_PANEL }, + }, +}; + +export { example as barClicksAndHovers } from './1_bar_clicks'; +export { example as areaPointClicksAndHovers } from './2_area_point_clicks'; +export { example as linePointClicksAndHovers } from './3_line_point_clicks'; +export { example as lineAreaBarPointClicksAndHovers } from './4_line_area_bar_clicks'; +export { example as clicksHoversOnLegendItemsBarChart } from './5_clicks_legend_items_bar'; +export { example as clickHoversOnLegendItemsAreaChart } from './6_clicks_legend_items_area'; +export { example as clickHoversOnLegendItemsLineChart } from './7_clicks_legend_items_line'; +export { example as clickHoversOnLegendItemsMixedChart } from './8_clicks_legend_items_mixed'; +export { example as brushSelectionToolOnLinear } from './9_brush_selection_linear'; + +export { example as brushSelectionToolOnBarChartLinear } from './10_brush_selection_bar'; +export { example as brushSelectionToolOnTimeCharts } from './11_brush_time'; +export { example as brushSelectionToolOnHistogramTimeCharts } from './12_brush_time_hist'; +export { example as brushDisabledOnOrdinalXAxis } from './13_brush_disabled_ordinal'; +export { example as crosshairWithTimeAxis } from './14_crosshair_time'; +export { example as renderChangeAction } from './15_render_change'; +export { example as cursorUpdateAction } from './16_cursor_update_action'; +export { example as pngExportAction } from './17_png_export'; diff --git a/stories/legend.tsx b/stories/legend.tsx deleted file mode 100644 index 0169923514..0000000000 --- a/stories/legend.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import { boolean, select, number } from '@storybook/addon-knobs'; -import React from 'react'; -import { - AreaSeries, - Axis, - BarSeries, - Chart, - CurveType, - getAxisId, - getSpecId, - LineSeries, - Position, - ScaleType, - Settings, - PartialTheme, -} from '../src/'; -import * as TestDatasets from '../src/utils/data_samples/test_dataset'; -import { TSVB_DATASET } from '../src/utils/data_samples/test_dataset_tsvb'; -import { arrayKnobs } from './common'; - -export default { - title: 'Legend', - parameters: { - info: { - source: false, - }, - }, -}; - -export const right = () => { - const yAccessors = ['y1', 'y2']; - const splitSeriesAccessors = ['g1', 'g2']; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -right.story = { - name: 'right', -}; - -export const bottom = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -bottom.story = { - name: 'bottom', -}; - -export const left = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -left.story = { - name: 'left', -}; - -export const top = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -top.story = { - name: 'top', -}; - -export const changingSpecs = () => { - const splitSeries = boolean('split series', true) ? ['g1', 'g2'] : undefined; - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -changingSpecs.story = { - name: 'changing specs', -}; - -export const hideLegendItemsBySeries = () => { - const hideBarSeriesInLegend = boolean('hide bar series in legend', false); - const hideLineSeriesInLegend = boolean('hide line series in legend', false); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -hideLegendItemsBySeries.story = { - name: 'hide legend items by series', -}; - -export const displayValuesInLegendElements = () => { - const showLegendDisplayValue = boolean('show display value in legend', true); - const legendPosition = select( - 'legendPosition', - { - right: Position.Right, - bottom: Position.Bottom, - left: Position.Left, - top: Position.Top, - }, - Position.Right, - ); - - const tsvbSeries = TSVB_DATASET.series; - - const namesArray = arrayKnobs('series names (in sort order)', ['jpg', 'php', 'png', 'css', 'gif']); - - const seriesComponents = tsvbSeries.map((series: any) => { - const nameIndex = namesArray.findIndex((name) => { - return name === series.label; - }); - const sortIndex = nameIndex > -1 ? nameIndex : undefined; - - return ( - - ); - }); - return ( - - - - Number(d).toFixed(2)} - /> - {seriesComponents} - - ); -}; -displayValuesInLegendElements.story = { - name: 'display values in legend elements', -}; - -export const legendSpacingBuffer = () => { - const theme: PartialTheme = { - legend: { - spacingBuffer: number('legend buffer value', 80), - }, - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -legendSpacingBuffer.story = { - name: 'legend spacingBuffer', -}; -// { -// info: { -// text: -// 'For high variability in values it may be necessary to increase the `spacingBuffer` to account for larger numbers.', -// }, -// }, -// ); diff --git a/stories/legend/1_legend_right.tsx b/stories/legend/1_legend_right.tsx new file mode 100644 index 0000000000..4dfc6a8fe0 --- /dev/null +++ b/stories/legend/1_legend_right.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const yAccessors = ['y1', 'y2']; + const splitSeriesAccessors = ['g1', 'g2']; + + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/legend/2_legend_bottom.tsx b/stories/legend/2_legend_bottom.tsx new file mode 100644 index 0000000000..bd15afea4c --- /dev/null +++ b/stories/legend/2_legend_bottom.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/legend/3_legend_left.tsx b/stories/legend/3_legend_left.tsx new file mode 100644 index 0000000000..c36c0ab4cc --- /dev/null +++ b/stories/legend/3_legend_left.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/legend/4_legend_top.tsx b/stories/legend/4_legend_top.tsx new file mode 100644 index 0000000000..6856e99f7a --- /dev/null +++ b/stories/legend/4_legend_top.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/legend/5_changing_specs.tsx b/stories/legend/5_changing_specs.tsx new file mode 100644 index 0000000000..093337935d --- /dev/null +++ b/stories/legend/5_changing_specs.tsx @@ -0,0 +1,25 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; + +export const example = () => { + const splitSeries = boolean('split series', true) ? ['g1', 'g2'] : undefined; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/legend/6_hide_legend.tsx b/stories/legend/6_hide_legend.tsx new file mode 100644 index 0000000000..08a64a4e1a --- /dev/null +++ b/stories/legend/6_hide_legend.tsx @@ -0,0 +1,47 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + const hideBarSeriesInLegend = boolean('hide bar series in legend', false); + const hideLineSeriesInLegend = boolean('hide line series in legend', false); + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/legend/7_display_values.tsx b/stories/legend/7_display_values.tsx new file mode 100644 index 0000000000..cb07593047 --- /dev/null +++ b/stories/legend/7_display_values.tsx @@ -0,0 +1,53 @@ +import { boolean, select } from '@storybook/addon-knobs'; +import React from 'react'; +import { AreaSeries, Axis, Chart, CurveType, Position, ScaleType, Settings } from '../../src/'; +import { TSVB_DATASET } from '../../src/utils/data_samples/test_dataset_tsvb'; +import { arrayKnobs } from '../utils/knobs'; + +export const example = () => { + const showLegendDisplayValue = boolean('show display value in legend', true); + const legendPosition = select( + 'legendPosition', + { + right: Position.Right, + bottom: Position.Bottom, + left: Position.Left, + top: Position.Top, + }, + Position.Right, + ); + + const tsvbSeries = TSVB_DATASET.series; + + const namesArray = arrayKnobs('series names (in sort order)', ['jpg', 'php', 'png', 'css', 'gif']); + + const seriesComponents = tsvbSeries.map((series: any) => { + const nameIndex = namesArray.findIndex((name) => { + return name === series.label; + }); + const sortIndex = nameIndex > -1 ? nameIndex : undefined; + + return ( + + ); + }); + return ( + + + + Number(d).toFixed(2)} /> + {seriesComponents} + + ); +}; diff --git a/stories/legend/8_spacing_buffer.tsx b/stories/legend/8_spacing_buffer.tsx new file mode 100644 index 0000000000..587dffca1e --- /dev/null +++ b/stories/legend/8_spacing_buffer.tsx @@ -0,0 +1,46 @@ +import { number } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, PartialTheme } from '../../src/'; + +export const example = () => { + const theme: PartialTheme = { + legend: { + spacingBuffer: number('legend buffer value', 80), + }, + }; + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/legend/legend.stories.tsx b/stories/legend/legend.stories.tsx new file mode 100644 index 0000000000..908565bd29 --- /dev/null +++ b/stories/legend/legend.stories.tsx @@ -0,0 +1,17 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Legend', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as right } from './1_legend_right'; +export { example as bottom } from './2_legend_bottom'; +export { example as left } from './3_legend_left'; +export { example as top } from './4_legend_top'; +export { example as changingSpecs } from './5_changing_specs'; +export { example as hideLegendItemsBySeries } from './6_hide_legend'; +export { example as displayValuesInLegendElements } from './7_display_values'; +export { example as legendSpacingBuffer } from './8_spacing_buffer'; diff --git a/stories/line/1_basic.tsx b/stories/line/1_basic.tsx new file mode 100644 index 0000000000..325505ad2a --- /dev/null +++ b/stories/line/1_basic.tsx @@ -0,0 +1,25 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { Chart, LineSeries, ScaleType } from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +export const example = () => { + const toggleSpec = boolean('toggle line spec', true); + const data1 = KIBANA_METRICS.metrics.kibana_os_load[0].data; + const data2 = data1.map((datum) => [datum[0], datum[1] - 1]); + const data = toggleSpec ? data1 : data2; + const specId = toggleSpec ? 'lines1' : 'lines2'; + + return ( + + + + ); +}; diff --git a/stories/line/2_w_axis.tsx b/stories/line/2_w_axis.tsx new file mode 100644 index 0000000000..3c25d42a6c --- /dev/null +++ b/stories/line/2_w_axis.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Axis, Chart, LineSeries, niceTimeFormatByDay, Position, ScaleType, timeFormatter } from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + `${Number(d).toFixed(2)}%`} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/line/3_ordinal.tsx b/stories/line/3_ordinal.tsx new file mode 100644 index 0000000000..08b695fd5a --- /dev/null +++ b/stories/line/3_ordinal.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Axis, Chart, LineSeries, niceTimeFormatByDay, Position, ScaleType, Settings, timeFormatter } from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { getChartRotationKnob } from '../utils/knobs'; +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(2)}%`} + /> + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; diff --git a/stories/line/4_linear.tsx b/stories/line/4_linear.tsx new file mode 100644 index 0000000000..2f7ae279d8 --- /dev/null +++ b/stories/line/4_linear.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, Chart, LineSeries, niceTimeFormatByDay, Position, ScaleType, timeFormatter } from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + `${Number(d).toFixed(2)}%`} + /> + + + ); +}; diff --git a/stories/line/5_w_axis_and_legend.tsx b/stories/line/5_w_axis_and_legend.tsx new file mode 100644 index 0000000000..c00f446fa1 --- /dev/null +++ b/stories/line/5_w_axis_and_legend.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Axis, Chart, LineSeries, niceTimeFormatByDay, Position, ScaleType, Settings, timeFormatter } from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(0)}%`} + /> + + + ); +}; diff --git a/stories/line/6_curved.tsx b/stories/line/6_curved.tsx new file mode 100644 index 0000000000..8aa12eddf3 --- /dev/null +++ b/stories/line/6_curved.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import { + Axis, + Chart, + CurveType, + LineSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(0)}%`} + /> + + + + + + + + + ); +}; diff --git a/stories/line/7_multiple.tsx b/stories/line/7_multiple.tsx new file mode 100644 index 0000000000..19788ea355 --- /dev/null +++ b/stories/line/7_multiple.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { + Axis, + Chart, + CurveType, + LineSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(0)}%`} + /> + + + + + + ); +}; diff --git a/stories/line/8_stacked.tsx b/stories/line/8_stacked.tsx new file mode 100644 index 0000000000..e809c3cf99 --- /dev/null +++ b/stories/line/8_stacked.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { + Axis, + Chart, + CurveType, + LineSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(0)}%`} + /> + + + + + ); +}; diff --git a/stories/line/9_multi_series.tsx b/stories/line/9_multi_series.tsx new file mode 100644 index 0000000000..3ae25f99aa --- /dev/null +++ b/stories/line/9_multi_series.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { + Axis, + Chart, + CurveType, + LineSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from '../../src/'; +import { KIBANA_METRICS } from '../../src/utils/data_samples/test_dataset_kibana'; +import { TSVB_DATASET } from '../../src/utils/data_samples/test_dataset_tsvb'; + +const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); + +export const example = () => { + return ( + + + + `${Number(d).toFixed(0)}%`} + /> + {TSVB_DATASET.series.map((series) => { + return ( + + ); + })} + + ); +}; diff --git a/stories/line/line.stories.tsx b/stories/line/line.stories.tsx new file mode 100644 index 0000000000..970c206e38 --- /dev/null +++ b/stories/line/line.stories.tsx @@ -0,0 +1,18 @@ +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export default { + title: 'Line Chart', + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; + +export { example as basic } from './1_basic'; +export { example as withAxis } from './2_w_axis'; +export { example as ordinalWithAxis } from './3_ordinal'; +export { example as linearWithAxis } from './4_linear'; +export { example as withAxisAndLegend } from './5_w_axis_and_legend'; +export { example as curvedWithAxisAndLegend } from './6_curved'; +export { example as multipleWithAxisAndLegend } from './7_multiple'; +export { example as stackedWithAxisAndLegend } from './8_stacked'; +export { example as multiSeriesWithLogValues } from './9_multi_series'; diff --git a/stories/line_chart.tsx b/stories/line_chart.tsx deleted file mode 100644 index c7f2a49e53..0000000000 --- a/stories/line_chart.tsx +++ /dev/null @@ -1,394 +0,0 @@ -import { boolean } from '@storybook/addon-knobs'; -import React from 'react'; -import { - Axis, - Chart, - CurveType, - getAxisId, - getSpecId, - LineSeries, - niceTimeFormatByDay, - Position, - ScaleType, - Settings, - timeFormatter, -} from '../src/'; -import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { TSVB_DATASET } from '../src/utils/data_samples/test_dataset_tsvb'; -import { getChartRotationKnob } from './common'; - -const dateFormatter = timeFormatter(niceTimeFormatByDay(1)); - -export default { - title: 'Line Chart', - parameters: { - info: { - source: false, - }, - }, -}; - -export const basic = () => { - const toggleSpec = boolean('toggle line spec', true); - const data1 = KIBANA_METRICS.metrics.kibana_os_load[0].data; - const data2 = data1.map((datum) => [datum[0], datum[1] - 1]); - const data = toggleSpec ? data1 : data2; - const specId = toggleSpec ? 'lines1' : 'lines2'; - - return ( - - - - ); -}; -basic.story = { - name: 'basic', -}; - -export const wAxis = () => { - return ( - - - `${Number(d).toFixed(2)}%`} - /> - - - ); -}; -wAxis.story = { - name: 'w axis', -}; - -export const ordinalWAxis = () => { - return ( - - - - `${Number(d).toFixed(2)}%`} - /> - - - ); -}; -ordinalWAxis.story = { - name: 'ordinal w axis', -}; - -export const linearWAxis = () => { - return ( - - - `${Number(d).toFixed(2)}%`} - /> - - - ); -}; -linearWAxis.story = { - name: 'linear w axis', -}; - -export const wAxisAndLegend = () => { - return ( - - - - `${Number(d).toFixed(0)}%`} - /> - - - ); -}; -wAxisAndLegend.story = { - name: 'w axis and legend', -}; - -export const curvedwaxisandlegend = () => { - return ( - - - - `${Number(d).toFixed(0)}%`} - /> - - - - - - - - - ); -}; -curvedwaxisandlegend.story = { - name: 'curved w axis and legend', -}; - -export const multiplewaxisandlegend = () => { - return ( - - - - `${Number(d).toFixed(0)}%`} - /> - - - - - - ); -}; -multiplewaxisandlegend.story = { - name: 'multiple w axis and legend', -}; - -export const stackedwaxisandlegend = () => { - return ( - - - - `${Number(d).toFixed(0)}%`} - /> - - - - - ); -}; -stackedwaxisandlegend.story = { - name: 'stacked w axis and legend', -}; - -export const multiserieswithlogvalues = () => { - return ( - - - - `${Number(d).toFixed(0)}%`} - /> - {TSVB_DATASET.series.map((series) => { - return ( - - ); - })} - - ); -}; -multiserieswithlogvalues.story = { - name: 'multi series with log values (limit 0 or negative values)', -}; diff --git a/stories/mixed.tsx b/stories/mixed.tsx deleted file mode 100644 index bbb27eacee..0000000000 --- a/stories/mixed.tsx +++ /dev/null @@ -1,481 +0,0 @@ -import { select, number } from '@storybook/addon-knobs'; -import { DateTime } from 'luxon'; -import React from 'react'; - -import { - AreaSeries, - Axis, - BarSeries, - Chart, - CurveType, - getAxisId, - getSpecId, - LineSeries, - Position, - ScaleType, - Settings, -} from '../src/'; -import { timeFormatter } from '../src/utils/data/formatters'; -import { Fit, SeriesTypes } from '../src/chart_types/xy_chart/utils/specs'; - -export default { - title: 'Mixed Charts', - parameters: { - info: { - source: false, - }, - }, -}; - -export const barsAndLines = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -barsAndLines.story = { - name: 'bar and lines', -}; - -export const linesAndAreas = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -linesAndAreas.story = { - name: 'lines and areas', -}; - -export const areasAndBars = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - Number(d).toFixed(2)} - /> - - - - ); -}; -areasAndBars.story = { - name: 'areas and bars', -}; - -export const testBarLinesLinear = () => { - const data1 = [ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 4], - [7, 3], - [8, 2], - [9, 1], - ]; - const data2 = [ - [1, 5], - [2, 4], - [3, 3], - [4, 2], - [5, 1], - [6, 2], - [7, 3], - [8, 4], - [9, 5], - ]; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -testBarLinesLinear.story = { - name: '[test] - bar/lines linear', -}; - -export const testBarLinesTime = () => { - const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); - const data1 = [ - [start.toMillis(), 1, 4], - [start.plus({ minute: 1 }).toMillis(), 2, 5], - [start.plus({ minute: 2 }).toMillis(), 3, 6], - [start.plus({ minute: 3 }).toMillis(), 4, 7], - [start.plus({ minute: 4 }).toMillis(), 5, 8], - [start.plus({ minute: 5 }).toMillis(), 4, 7], - [start.plus({ minute: 6 }).toMillis(), 3, 6], - [start.plus({ minute: 7 }).toMillis(), 2, 5], - [start.plus({ minute: 8 }).toMillis(), 1, 4], - ]; - const data2 = [ - [start.toMillis(), 1, 4], - [start.plus({ minute: 1 }).toMillis(), 2, 5], - [start.plus({ minute: 2 }).toMillis(), 3, 6], - [start.plus({ minute: 3 }).toMillis(), 4, 7], - [start.plus({ minute: 4 }).toMillis(), 5, 8], - [start.plus({ minute: 5 }).toMillis(), 4, 7], - [start.plus({ minute: 6 }).toMillis(), 3, 6], - [start.plus({ minute: 7 }).toMillis(), 2, 5], - [start.plus({ minute: 8 }).toMillis(), 1, 4], - ]; - const dateFormatter = timeFormatter('HH:mm:ss'); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -testBarLinesTime.story = { - name: '[test] - bar/lines time', -}; - -export const fittingFunctionsNonStackedSeries = () => { - const dataTypes = { - isolated: [ - { x: 0, y: 3 }, - { x: 1, y: 5 }, - { x: 2, y: null }, - { x: 3, y: 4 }, - { x: 4, y: null }, - { x: 5, y: 5 }, - { x: 6, y: null }, - { x: 7, y: 12 }, - { x: 8, y: null }, - { x: 9, y: 10 }, - { x: 10, y: 7 }, - ], - successive: [ - { x: 0, y: 3 }, - { x: 1, y: 5 }, - { x: 2, y: null }, - { x: 4, y: null }, - { x: 6, y: null }, - { x: 8, y: null }, - { x: 9, y: 10 }, - { x: 10, y: 7 }, - ], - endPoints: [ - { x: 0, y: null }, - { x: 1, y: 5 }, - { x: 3, y: 4 }, - { x: 5, y: 5 }, - { x: 7, y: 12 }, - { x: 9, y: 10 }, - { x: 10, y: null }, - ], - ordinal: [ - { x: 'a', y: null }, - { x: 'b', y: 3 }, - { x: 'c', y: 5 }, - { x: 'd', y: null }, - { x: 'e', y: 4 }, - { x: 'f', y: null }, - { x: 'g', y: 5 }, - { x: 'h', y: 6 }, - { x: 'i', y: null }, - { x: 'j', y: null }, - { x: 'k', y: null }, - { x: 'l', y: 12 }, - { x: 'm', y: null }, - ], - all: [ - { x: 0, y: null }, - { x: 1, y: 3 }, - { x: 2, y: 5 }, - { x: 3, y: null }, - { x: 4, y: 4 }, - { x: 5, y: null }, - { x: 6, y: 5 }, - { x: 7, y: 6 }, - { x: 8, y: null }, - { x: 9, y: null }, - { x: 10, y: null }, - { x: 11, y: 12 }, - { x: 12, y: null }, - ], - }; - - const seriesType = select( - 'seriesType', - { - Area: SeriesTypes.Area, - Line: SeriesTypes.Line, - }, - SeriesTypes.Area, - ); - const dataKey = select( - 'dataset', - { - 'Isolated Points': 'isolated', - 'Successive null Points': 'successive', - 'null end points': 'endPoints', - 'Ordinal x values': 'ordinal', - 'All edge cases': 'all', - }, - 'all', - ); - // @ts-ignore - const dataset = dataTypes[dataKey]; - const fit = select( - 'fitting function', - { - None: Fit.None, - Carry: Fit.Carry, - Lookahead: Fit.Lookahead, - Nearest: Fit.Nearest, - Average: Fit.Average, - Linear: Fit.Linear, - Zero: Fit.Zero, - Explicit: Fit.Explicit, - }, - Fit.Average, - ); - const curve = select( - 'Curve', - { - 'Curve cardinal': CurveType.CURVE_CARDINAL, - 'Curve natural': CurveType.CURVE_NATURAL, - 'Curve monotone x': CurveType.CURVE_MONOTONE_X, - 'Curve monotone y': CurveType.CURVE_MONOTONE_Y, - 'Curve basis': CurveType.CURVE_BASIS, - 'Curve catmull rom': CurveType.CURVE_CATMULL_ROM, - 'Curve step': CurveType.CURVE_STEP, - 'Curve step after': CurveType.CURVE_STEP_AFTER, - 'Curve step before': CurveType.CURVE_STEP_BEFORE, - Linear: CurveType.LINEAR, - }, - 0, - ); - const endValue = select( - 'End value', - { - None: 'none', - nearest: 'nearest', - '0': 0, - '2': 2, - }, - 'none', - ); - const parsedEndValue: number | 'nearest' = Number.isNaN(Number(endValue)) ? 'nearest' : Number(endValue); - const value = number('Explicit valuve (using Fit.Explicit)', 5); - const xScaleType = dataKey === 'ordinal' ? ScaleType.Ordinal : ScaleType.Linear; - - return ( - - - - - {seriesType === SeriesTypes.Area ? ( - - ) : ( - - )} - - ); -}; -fittingFunctionsNonStackedSeries.story = { - name: 'Fitting functions - non-stacked series', -}; diff --git a/stories/mixed/1_bars_and_lines.tsx b/stories/mixed/1_bars_and_lines.tsx new file mode 100644 index 0000000000..39b42dfcf5 --- /dev/null +++ b/stories/mixed/1_bars_and_lines.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/mixed/2_lines_and_areas.tsx b/stories/mixed/2_lines_and_areas.tsx new file mode 100644 index 0000000000..5f30c8ea70 --- /dev/null +++ b/stories/mixed/2_lines_and_areas.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import { AreaSeries, Axis, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/mixed/3_areas_and_bars.tsx b/stories/mixed/3_areas_and_bars.tsx new file mode 100644 index 0000000000..08cd249cdd --- /dev/null +++ b/stories/mixed/3_areas_and_bars.tsx @@ -0,0 +1,46 @@ +import React from 'react'; + +import { AreaSeries, Axis, BarSeries, Chart, CurveType, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/mixed/4_test_bar.tsx b/stories/mixed/4_test_bar.tsx new file mode 100644 index 0000000000..1d479b2b83 --- /dev/null +++ b/stories/mixed/4_test_bar.tsx @@ -0,0 +1,53 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + const data1 = [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 4], + [7, 3], + [8, 2], + [9, 1], + ]; + const data2 = [ + [1, 5], + [2, 4], + [3, 3], + [4, 2], + [5, 1], + [6, 2], + [7, 3], + [8, 4], + [9, 5], + ]; + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/mixed/5_test_bar_time.tsx b/stories/mixed/5_test_bar_time.tsx new file mode 100644 index 0000000000..de24acb4a2 --- /dev/null +++ b/stories/mixed/5_test_bar_time.tsx @@ -0,0 +1,63 @@ +import { DateTime } from 'luxon'; +import React from 'react'; + +import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '../../src/'; +import { timeFormatter } from '../../src/utils/data/formatters'; + +export const example = () => { + const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); + const data1 = [ + [start.toMillis(), 1, 4], + [start.plus({ minute: 1 }).toMillis(), 2, 5], + [start.plus({ minute: 2 }).toMillis(), 3, 6], + [start.plus({ minute: 3 }).toMillis(), 4, 7], + [start.plus({ minute: 4 }).toMillis(), 5, 8], + [start.plus({ minute: 5 }).toMillis(), 4, 7], + [start.plus({ minute: 6 }).toMillis(), 3, 6], + [start.plus({ minute: 7 }).toMillis(), 2, 5], + [start.plus({ minute: 8 }).toMillis(), 1, 4], + ]; + const data2 = [ + [start.toMillis(), 1, 4], + [start.plus({ minute: 1 }).toMillis(), 2, 5], + [start.plus({ minute: 2 }).toMillis(), 3, 6], + [start.plus({ minute: 3 }).toMillis(), 4, 7], + [start.plus({ minute: 4 }).toMillis(), 5, 8], + [start.plus({ minute: 5 }).toMillis(), 4, 7], + [start.plus({ minute: 6 }).toMillis(), 3, 6], + [start.plus({ minute: 7 }).toMillis(), 2, 5], + [start.plus({ minute: 8 }).toMillis(), 1, 4], + ]; + const dateFormatter = timeFormatter('HH:mm:ss'); + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/mixed/6_fitting.tsx b/stories/mixed/6_fitting.tsx new file mode 100644 index 0000000000..21c164d606 --- /dev/null +++ b/stories/mixed/6_fitting.tsx @@ -0,0 +1,192 @@ +import { select, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AreaSeries, Axis, Chart, CurveType, LineSeries, Position, ScaleType, Settings } from '../../src/'; +import { Fit, SeriesTypes } from '../../src/chart_types/xy_chart/utils/specs'; +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export const example = () => { + const dataTypes = { + isolated: [ + { x: 0, y: 3 }, + { x: 1, y: 5 }, + { x: 2, y: null }, + { x: 3, y: 4 }, + { x: 4, y: null }, + { x: 5, y: 5 }, + { x: 6, y: null }, + { x: 7, y: 12 }, + { x: 8, y: null }, + { x: 9, y: 10 }, + { x: 10, y: 7 }, + ], + successive: [ + { x: 0, y: 3 }, + { x: 1, y: 5 }, + { x: 2, y: null }, + { x: 4, y: null }, + { x: 6, y: null }, + { x: 8, y: null }, + { x: 9, y: 10 }, + { x: 10, y: 7 }, + ], + endPoints: [ + { x: 0, y: null }, + { x: 1, y: 5 }, + { x: 3, y: 4 }, + { x: 5, y: 5 }, + { x: 7, y: 12 }, + { x: 9, y: 10 }, + { x: 10, y: null }, + ], + ordinal: [ + { x: 'a', y: null }, + { x: 'b', y: 3 }, + { x: 'c', y: 5 }, + { x: 'd', y: null }, + { x: 'e', y: 4 }, + { x: 'f', y: null }, + { x: 'g', y: 5 }, + { x: 'h', y: 6 }, + { x: 'i', y: null }, + { x: 'j', y: null }, + { x: 'k', y: null }, + { x: 'l', y: 12 }, + { x: 'm', y: null }, + ], + all: [ + { x: 0, y: null }, + { x: 1, y: 3 }, + { x: 2, y: 5 }, + { x: 3, y: null }, + { x: 4, y: 4 }, + { x: 5, y: null }, + { x: 6, y: 5 }, + { x: 7, y: 6 }, + { x: 8, y: null }, + { x: 9, y: null }, + { x: 10, y: null }, + { x: 11, y: 12 }, + { x: 12, y: null }, + ], + }; + + const seriesType = select( + 'seriesType', + { + Area: SeriesTypes.Area, + Line: SeriesTypes.Line, + }, + SeriesTypes.Area, + ); + const dataKey = select( + 'dataset', + { + 'Isolated Points': 'isolated', + 'Successive null Points': 'successive', + 'null end points': 'endPoints', + 'Ordinal x values': 'ordinal', + 'All edge cases': 'all', + }, + 'all', + ); + // @ts-ignore + const dataset = dataTypes[dataKey]; + const fit = select( + 'fitting function', + { + None: Fit.None, + Carry: Fit.Carry, + Lookahead: Fit.Lookahead, + Nearest: Fit.Nearest, + Average: Fit.Average, + Linear: Fit.Linear, + Zero: Fit.Zero, + Explicit: Fit.Explicit, + }, + Fit.Average, + ); + const curve = select( + 'Curve', + { + 'Curve cardinal': CurveType.CURVE_CARDINAL, + 'Curve natural': CurveType.CURVE_NATURAL, + 'Curve monotone x': CurveType.CURVE_MONOTONE_X, + 'Curve monotone y': CurveType.CURVE_MONOTONE_Y, + 'Curve basis': CurveType.CURVE_BASIS, + 'Curve catmull rom': CurveType.CURVE_CATMULL_ROM, + 'Curve step': CurveType.CURVE_STEP, + 'Curve step after': CurveType.CURVE_STEP_AFTER, + 'Curve step before': CurveType.CURVE_STEP_BEFORE, + Linear: CurveType.LINEAR, + }, + 0, + ); + const endValue = select( + 'End value', + { + None: 'none', + nearest: 'nearest', + '0': 0, + '2': 2, + }, + 'none', + ); + const parsedEndValue: number | 'nearest' = Number.isNaN(Number(endValue)) ? 'nearest' : Number(endValue); + const value = number('Explicit valuve (using Fit.Explicit)', 5); + const xScaleType = dataKey === 'ordinal' ? ScaleType.Ordinal : ScaleType.Linear; + + return ( + + + + + {seriesType === SeriesTypes.Area ? ( + + ) : ( + + )} + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; diff --git a/stories/mixed/mixed.stories.tsx b/stories/mixed/mixed.stories.tsx new file mode 100644 index 0000000000..fc2a98b816 --- /dev/null +++ b/stories/mixed/mixed.stories.tsx @@ -0,0 +1,15 @@ +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export default { + title: 'Mixed Charts', + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; + +export { example as barsAndLines } from './1_bars_and_lines'; +export { example as linesAndAreas } from './2_lines_and_areas'; +export { example as areasAndBars } from './3_areas_and_bars'; +export { example as testBarLinesLinear } from './4_test_bar'; +export { example as testBarLinesTime } from './5_test_bar_time'; +export { example as fittingFunctionsNonStackedSeries } from './6_fitting'; diff --git a/stories/rotations.tsx b/stories/rotations.tsx deleted file mode 100644 index 2569c278a4..0000000000 --- a/stories/rotations.tsx +++ /dev/null @@ -1,299 +0,0 @@ -import { boolean, select } from '@storybook/addon-knobs'; -import React from 'react'; -import { Axis, BarSeries, Chart, getAxisId, getSpecId, Position, ScaleType, Settings } from '../src/'; - -export default { - title: 'Rotations', - parameters: { - info: { - source: false, - }, - }, -}; - -export const withOrdinalAxis = () => { - return ( - - - - - - - - ); -}; -withOrdinalAxis.story = { - name: 'with ordinal axis', -}; - -export const negative90DegreeOrdinal = () => { - return ( - - - - - - - - - ); -}; -negative90DegreeOrdinal.story = { - name: 'negative 90 deg ordinal', -}; - -export const rotations0DegOrdinal = () => { - return ( - - - - - - - - - ); -}; -rotations0DegOrdinal.story = { - name: '0 deg ordinal', -}; - -export const rotations90DegOrdinal = () => { - return ( - - - - - - - - - ); -}; -rotations90DegOrdinal.story = { - name: '90 deg ordinal', -}; - -export const rotations180DegOrdinal = () => { - return ( - - - - - - - - - ); -}; -rotations180DegOrdinal.story = { - name: '180 deg ordinal', -}; - -export const negative90DegLinear = () => { - return ( - - - - - - - - - ); -}; -negative90DegLinear.story = { - name: 'negative 90 deg linear', -}; - -export const rotations0DegLinear = () => { - return ( - - - - - - - - - ); -}; -rotations0DegLinear.story = { - name: '0 deg linear', -}; - -export const rotations90DegLinear = () => { - return ( - - - - - - - - - ); -}; -rotations90DegLinear.story = { - name: '90 deg linear', -}; - -export const rotations180DegLinear = () => { - return ( - - - - - - - - - ); -}; -rotations180DegLinear.story = { - name: '180 deg linear', -}; diff --git a/stories/rotations/1_ordinal.tsx b/stories/rotations/1_ordinal.tsx new file mode 100644 index 0000000000..85d533c8f0 --- /dev/null +++ b/stories/rotations/1_ordinal.tsx @@ -0,0 +1,72 @@ +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +import { boolean, select } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/rotations/2_negative_ordinal.tsx b/stories/rotations/2_negative_ordinal.tsx new file mode 100644 index 0000000000..7254aab88b --- /dev/null +++ b/stories/rotations/2_negative_ordinal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/3_rotations_ordinal.tsx b/stories/rotations/3_rotations_ordinal.tsx new file mode 100644 index 0000000000..d296132470 --- /dev/null +++ b/stories/rotations/3_rotations_ordinal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/4_90_ordinal.tsx b/stories/rotations/4_90_ordinal.tsx new file mode 100644 index 0000000000..1604c2ad82 --- /dev/null +++ b/stories/rotations/4_90_ordinal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/5_180_ordinal.tsx b/stories/rotations/5_180_ordinal.tsx new file mode 100644 index 0000000000..f779eabbf8 --- /dev/null +++ b/stories/rotations/5_180_ordinal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/6_negative_linear.tsx b/stories/rotations/6_negative_linear.tsx new file mode 100644 index 0000000000..1ec1a416bd --- /dev/null +++ b/stories/rotations/6_negative_linear.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/7_rotations_linear.tsx b/stories/rotations/7_rotations_linear.tsx new file mode 100644 index 0000000000..605f761ec5 --- /dev/null +++ b/stories/rotations/7_rotations_linear.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/8_90_deg_linear.tsx b/stories/rotations/8_90_deg_linear.tsx new file mode 100644 index 0000000000..cbc54ac895 --- /dev/null +++ b/stories/rotations/8_90_deg_linear.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/9_180_deg_linear.tsx b/stories/rotations/9_180_deg_linear.tsx new file mode 100644 index 0000000000..f6f7e8b4e3 --- /dev/null +++ b/stories/rotations/9_180_deg_linear.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src/'; + +export const example = () => { + return ( + + + + + + + + + ); +}; diff --git a/stories/rotations/rotations.stories.tsx b/stories/rotations/rotations.stories.tsx new file mode 100644 index 0000000000..af029a0d3e --- /dev/null +++ b/stories/rotations/rotations.stories.tsx @@ -0,0 +1,18 @@ +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export default { + title: 'Rotations', + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; + +export { example as withOrdinalAxis } from './1_ordinal'; +export { example as negative90DegreeOrdinal } from './2_negative_ordinal'; +export { example as rotations0DegOrdinal } from './3_rotations_ordinal'; +export { example as rotations90DegOrdinal } from './4_90_ordinal'; +export { example as rotations180DegOrdinal } from './5_180_ordinal'; +export { example as negative90DegLinear } from './6_negative_linear'; +export { example as rotations0DegLinear } from './7_rotations_linear'; +export { example as rotations90DegLinear } from './8_90_deg_linear'; +export { example as rotations180DegLinear } from './9_180_deg_linear'; diff --git a/stories/scales.tsx b/stories/scales.tsx deleted file mode 100644 index 09ab70efc8..0000000000 --- a/stories/scales.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import { select, boolean } from '@storybook/addon-knobs'; -import { DateTime } from 'luxon'; -import React from 'react'; -import { Axis, Chart, getAxisId, getSpecId, LineSeries, Position, ScaleType, Settings } from '../src'; - -const today = new Date().getTime(); -const UTC_DATE = DateTime.fromISO('2019-01-01T00:00:00.000Z').toMillis(); -const UTC_PLUS8_DATE = DateTime.fromISO('2019-01-01T00:00:00.000+08:00', { - setZone: true, -}).toMillis(); -const UTC_MINUS8_DATE = DateTime.fromISO('2019-01-01T00:00:00.000-08:00', { - setZone: true, -}).toMillis(); -const DAY_INCREMENT_1 = 1000 * 60 * 60 * 24; -const UTC_DATASET = new Array(10).fill(0).map((d, i) => { - return [UTC_DATE + DAY_INCREMENT_1 * i, i % 5]; -}); -const CURRENT_TIMEZONE_DATASET = new Array(10).fill(0).map((d, i) => { - return [today + DAY_INCREMENT_1 * i, i % 5]; -}); -const OTHER_PLUS8_TIMEZONE_DATASET = new Array(10).fill(0).map((d, i) => { - return [UTC_PLUS8_DATE + DAY_INCREMENT_1 * i, i % 5]; -}); -const OTHER_MINUS8_TIMEZONE_DATASET = new Array(10).fill(0).map((d, i) => { - return [UTC_MINUS8_DATE + DAY_INCREMENT_1 * i, i % 5]; -}); - -export default { - title: 'Scales', - parameters: { - info: { - source: false, - }, - }, -}; - -export const lineChartWithDifferentTimezones = () => { - const timezones = { - utc: 'utc', - local: 'local', - utcplus8: 'utc+8', - utcminus8: 'utc-8', - }; - const datasetSelected = select('dataset', timezones, 'utc'); - const tooltipSelected = select('tooltip', timezones, 'utc'); - - let data; - switch (datasetSelected) { - case 'local': - data = CURRENT_TIMEZONE_DATASET; - break; - case 'utc+8': - data = OTHER_PLUS8_TIMEZONE_DATASET; - break; - case 'utc-8': - data = OTHER_MINUS8_TIMEZONE_DATASET; - break; - case 'utc': - default: - data = UTC_DATASET; - break; - } - let tooltipFn: (d: number) => string; - switch (tooltipSelected) { - case 'local': - tooltipFn = (d: number) => { - return DateTime.fromMillis(d).toFormat('yyyy-MM-dd HH:mm:ss'); - }; - break; - case 'utc+8': - tooltipFn = (d: number) => { - return DateTime.fromMillis(d, { zone: 'utc+8' }).toFormat('yyyy-MM-dd HH:mm:ss'); - }; - break; - case 'utc-8': - tooltipFn = (d: number) => { - return DateTime.fromMillis(d, { zone: 'utc-8' }).toFormat('yyyy-MM-dd HH:mm:ss'); - }; - break; - default: - case 'utc': - tooltipFn = (d: number) => { - return DateTime.fromMillis(d) - .toUTC() - .toFormat('yyyy-MM-dd HH:mm:ss'); - }; - break; - } - return ( - - - - - - ); -}; -lineChartWithDifferentTimezones.story = { - name: 'line chart with different timezones', -}; - -export const xScaleUTCTimezoneLocalTooltip = () => { - return ( - - { - return DateTime.fromMillis(d).toFormat('yyyy-MM-dd HH:mm:ss'); - }} - /> - - - - ); -}; -xScaleUTCTimezoneLocalTooltip.story = { - name: 'x scale: UTC Time zone - local tooltip', -}; -// { -// info: { -// text: `If your data is in UTC timezone, your tooltip and axis labels can -// be configured to visualize the time translated to your local timezone. You should -// be able to see the first value on \`2019-01-01 01:00:00.000 \``, -// }, -// }, -// ) - -export const xScaleUTCTimezoneUTCTooltip = () => { - return ( - - { - return DateTime.fromMillis(d) - .toUTC() - .toFormat('yyyy-MM-dd HH:mm:ss'); - }} - /> - - - - ); -}; -xScaleUTCTimezoneUTCTooltip.story = { - name: 'x scale: UTC Time zone - UTC tooltip', -}; -// { -// info: { -// text: `The default timezone is UTC. If you want to visualize data in UTC, -// but you are in a different timezone, remember to format the millis from \`tickFormat\` -// to UTC. In this example be able to see the first value on \`2019-01-01 00:00:00.000 \``, -// }, -// }, -// ) - -export const xScaleYearScaleCustomTimezoneSameToneTooltip = () => { - return ( - - { - return DateTime.fromMillis(d, { zone: 'utc-6' }).toISO(); - // return DateTime.fromMillis(d, { zone: 'utc-6' }).toISO(); - }} - /> - - - - ); -}; -xScaleYearScaleCustomTimezoneSameToneTooltip.story = { - name: 'x scale year scale: custom timezone - same zone tooltip', -}; -// { -// info: { -// text: `You can visualize data in a different timezone than your local or UTC zones. -// Specify the \`timeZone={'utc-6'}\` property with the correct timezone and -// remember to apply the same timezone also to each formatted tick in \`tickFormat\` `, -// }, -// }, -// ) - -export const removeDuplicateScales = () => { - return ( - - - - `${d}%`} /> - `${d}%`} /> - `${d}%`} /> - `${d}%`} /> - - - ); -}; -removeDuplicateScales.story = { - name: 'Remove duplicate scales', -}; -// { -// info: { -// text: '`hideDuplicateAxes` will remove redundant axes that have the same min and max labels and position', -// }, -// }, -// ); diff --git a/stories/scales/1_different_timezones.tsx b/stories/scales/1_different_timezones.tsx new file mode 100644 index 0000000000..b5c4f461b1 --- /dev/null +++ b/stories/scales/1_different_timezones.tsx @@ -0,0 +1,95 @@ +import { select } from '@storybook/addon-knobs'; +import { DateTime } from 'luxon'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType } from '../../src'; + +const today = new Date().getTime(); +const UTC_DATE = DateTime.fromISO('2019-01-01T00:00:00.000Z').toMillis(); +const UTC_PLUS8_DATE = DateTime.fromISO('2019-01-01T00:00:00.000+08:00', { + setZone: true, +}).toMillis(); +const UTC_MINUS8_DATE = DateTime.fromISO('2019-01-01T00:00:00.000-08:00', { + setZone: true, +}).toMillis(); +const DAY_INCREMENT_1 = 1000 * 60 * 60 * 24; +const UTC_DATASET = new Array(10).fill(0).map((d, i) => { + return [UTC_DATE + DAY_INCREMENT_1 * i, i % 5]; +}); +const CURRENT_TIMEZONE_DATASET = new Array(10).fill(0).map((d, i) => { + return [today + DAY_INCREMENT_1 * i, i % 5]; +}); +const OTHER_PLUS8_TIMEZONE_DATASET = new Array(10).fill(0).map((d, i) => { + return [UTC_PLUS8_DATE + DAY_INCREMENT_1 * i, i % 5]; +}); +const OTHER_MINUS8_TIMEZONE_DATASET = new Array(10).fill(0).map((d, i) => { + return [UTC_MINUS8_DATE + DAY_INCREMENT_1 * i, i % 5]; +}); + +export const example = () => { + const timezones = { + utc: 'utc', + local: 'local', + utcplus8: 'utc+8', + utcminus8: 'utc-8', + }; + const datasetSelected = select('dataset', timezones, 'utc'); + const tooltipSelected = select('tooltip', timezones, 'utc'); + + let data; + switch (datasetSelected) { + case 'local': + data = CURRENT_TIMEZONE_DATASET; + break; + case 'utc+8': + data = OTHER_PLUS8_TIMEZONE_DATASET; + break; + case 'utc-8': + data = OTHER_MINUS8_TIMEZONE_DATASET; + break; + case 'utc': + default: + data = UTC_DATASET; + break; + } + let tooltipFn: (d: number) => string; + switch (tooltipSelected) { + case 'local': + tooltipFn = (d: number) => { + return DateTime.fromMillis(d).toFormat('yyyy-MM-dd HH:mm:ss'); + }; + break; + case 'utc+8': + tooltipFn = (d: number) => { + return DateTime.fromMillis(d, { zone: 'utc+8' }).toFormat('yyyy-MM-dd HH:mm:ss'); + }; + break; + case 'utc-8': + tooltipFn = (d: number) => { + return DateTime.fromMillis(d, { zone: 'utc-8' }).toFormat('yyyy-MM-dd HH:mm:ss'); + }; + break; + default: + case 'utc': + tooltipFn = (d: number) => { + return DateTime.fromMillis(d) + .toUTC() + .toFormat('yyyy-MM-dd HH:mm:ss'); + }; + break; + } + return ( + + + + + + ); +}; diff --git a/stories/scales/2_local_tooltip.tsx b/stories/scales/2_local_tooltip.tsx new file mode 100644 index 0000000000..46826b6a95 --- /dev/null +++ b/stories/scales/2_local_tooltip.tsx @@ -0,0 +1,45 @@ +import { DateTime } from 'luxon'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const UTC_DATE = DateTime.fromISO('2019-01-01T00:00:00.000Z').toMillis(); +const DAY_INCREMENT_1 = 1000 * 60 * 60 * 24; +const UTC_DATASET = new Array(10).fill(0).map((d, i) => { + return [UTC_DATE + DAY_INCREMENT_1 * i, i % 5]; +}); + +export const example = () => { + return ( + + { + return DateTime.fromMillis(d).toFormat('yyyy-MM-dd HH:mm:ss'); + }} + /> + + + + ); +}; + +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + info: { + text: `If your data is in UTC timezone, your tooltip and axis labels can be configured + to visualize the time translated to your local timezone. You should be able to see the + first value on \`2019-01-01 01:00:00.000 \``, + }, + }, +}; diff --git a/stories/scales/3_utc_tooltip.tsx b/stories/scales/3_utc_tooltip.tsx new file mode 100644 index 0000000000..74088cce30 --- /dev/null +++ b/stories/scales/3_utc_tooltip.tsx @@ -0,0 +1,47 @@ +import { DateTime } from 'luxon'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const UTC_DATE = DateTime.fromISO('2019-01-01T00:00:00.000Z').toMillis(); +const DAY_INCREMENT_1 = 1000 * 60 * 60 * 24; +const UTC_DATASET = new Array(10).fill(0).map((d, i) => { + return [UTC_DATE + DAY_INCREMENT_1 * i, i % 5]; +}); + +export const example = () => { + return ( + + { + return DateTime.fromMillis(d) + .toUTC() + .toFormat('yyyy-MM-dd HH:mm:ss'); + }} + /> + + + + ); +}; + +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + info: { + text: `The default timezone is UTC. If you want to visualize data in UTC, + but you are in a different timezone, remember to format the millis from \`tickFormat\` + to UTC. In this example be able to see the first value on \`2019-01-01 00:00:00.000 \``, + }, + }, +}; diff --git a/stories/scales/4_specified_timezone.tsx b/stories/scales/4_specified_timezone.tsx new file mode 100644 index 0000000000..f9c3d655c7 --- /dev/null +++ b/stories/scales/4_specified_timezone.tsx @@ -0,0 +1,45 @@ +import { DateTime } from 'luxon'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType } from '../../src'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + { + return DateTime.fromMillis(d, { zone: 'utc-6' }).toISO(); + }} + /> + + + + ); +}; + +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + info: { + text: `You can visualize data in a different timezone than your local or UTC zones. + Specify the \`timeZone={'utc-6'}\` property with the correct timezone and + remember to apply the same timezone also to each formatted tick in \`tickFormat\``, + }, + }, +}; diff --git a/stories/scales/5_remove_duplicates.tsx b/stories/scales/5_remove_duplicates.tsx new file mode 100644 index 0000000000..e6e0748f74 --- /dev/null +++ b/stories/scales/5_remove_duplicates.tsx @@ -0,0 +1,39 @@ +import { boolean } from '@storybook/addon-knobs'; +import React from 'react'; +import { Axis, Chart, LineSeries, Position, ScaleType, Settings } from '../../src'; + +export const example = () => { + return ( + + + + `${d}%`} /> + `${d}%`} /> + `${d}%`} /> + `${d}%`} /> + + + ); +}; + +example.story = { + parameters: { + info: { + text: `hideDuplicateAxes will remove redundant axes that have the same min and max labels and position`, + }, + }, +}; diff --git a/stories/scales/scales.stories.tsx b/stories/scales/scales.stories.tsx new file mode 100644 index 0000000000..187b7b6ea3 --- /dev/null +++ b/stories/scales/scales.stories.tsx @@ -0,0 +1,14 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Scales', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as timezoneConfiguration } from './1_different_timezones'; +export { example as tooltipInLocalTimezone } from './2_local_tooltip'; +export { example as tooltipInUTC } from './3_utc_tooltip'; +export { example as specifiedTimezone } from './4_specified_timezone'; +export { example as removeDuplicateAxis } from './5_remove_duplicates'; diff --git a/stories/styling.tsx b/stories/styling.tsx deleted file mode 100644 index cb27eec69d..0000000000 --- a/stories/styling.tsx +++ /dev/null @@ -1,1226 +0,0 @@ -import { boolean, color, number, select } from '@storybook/addon-knobs'; -import React from 'react'; - -import { switchTheme } from '../.storybook/theme_service'; -import { - AreaSeries, - Axis, - BarSeries, - Chart, - CurveType, - CustomSeriesColors, - DEFAULT_MISSING_COLOR, - getAxisId, - getSpecId, - LineSeries, - PartialTheme, - Position, - ScaleType, - Settings, - LineSeriesStyle, - TooltipType, - RecursivePartial, - Theme, - LIGHT_THEME, - DARK_THEME, - BarSeriesStyle, - PointStyle, -} from '../src/'; -import { SeededDataGenerator } from '../src/mocks/utils'; -import * as TestDatasets from '../src/utils/data_samples/test_dataset'; -import { palettes } from '../src/utils/themes/colors'; -import { - BarStyleAccessor, - PointStyleAccessor, - SeriesNameConfigOptions, - SeriesNameFn, -} from '../src/chart_types/xy_chart/utils/specs'; -import moment from 'moment'; -import { DateTime } from 'luxon'; - -function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { - return number( - title, - value, - { - range: true, - min, - max, - step, - }, - groupId, - ); -} - -function generateLineSeriesStyleKnobs( - groupName: string, - tag: string, - pointFill?: string, - pointStroke?: string, - pointStrokeWidth?: number, - pointRadius?: number, - lineStrokeWidth?: number, - lineStroke?: string, -): LineSeriesStyle { - return { - line: { - stroke: lineStroke ? color(`line.stroke (${tag})`, lineStroke, groupName) : undefined, - strokeWidth: range(`line.strokeWidth (${tag})`, 0, 10, lineStrokeWidth ? lineStrokeWidth : 1, groupName), - visible: boolean(`line.visible (${tag})`, true, groupName), - opacity: range(`line.opacity (${tag})`, 0, 1, 1, groupName, 0.01), - }, - point: { - visible: boolean(`point.visible (${tag})`, true, groupName), - radius: range(`point.radius (${tag})`, 0, 20, pointRadius ? pointRadius : 5, groupName, 0.5), - opacity: range(`point.opacity (${tag})`, 0, 1, 1, groupName, 0.01), - stroke: color(`point.stroke (${tag})`, pointStroke ? pointStroke : 'black', groupName), - fill: color(`point.fill (${tag})`, pointFill ? pointFill : 'lightgray', groupName), - strokeWidth: range(`point.strokeWidth (${tag})`, 0, 5, pointStrokeWidth ? pointStrokeWidth : 2, groupName, 0.01), - }, - }; -} - -function generateAreaSeriesStyleKnobs( - groupName: string, - tag: string, - pointFill?: string, - pointStroke?: string, - pointStrokeWidth?: number, - pointRadius?: number, - lineStrokeWidth?: number, - lineStroke?: string, - areaFill?: string, -) { - return { - ...generateLineSeriesStyleKnobs( - groupName, - tag, - pointFill, - pointStroke, - pointStrokeWidth, - pointRadius, - lineStrokeWidth, - lineStroke, - ), - area: { - fill: areaFill ? color(`area.fill (${tag})`, areaFill, groupName) : undefined, - visible: boolean(`area.visible (${tag})`, true, groupName), - opacity: range(`area.opacity (${tag})`, 0, 1, 0.8, groupName, 0.01), - }, - }; -} - -const dg = new SeededDataGenerator(); -const data1 = dg.generateGroupedSeries(40, 4); -const data2 = dg.generateSimpleSeries(40); -const data3 = dg.generateSimpleSeries(40); - -export default { - title: 'Stylings', - parameters: { - info: { - source: false, - }, - }, -}; - -export const chartSize = () => { - const theme: RecursivePartial = { - chartMargins: { - bottom: 0, - left: 0, - top: 0, - right: 0, - }, - }; - return ( -
- - - - - - - - - - - - - - - - - - - - -
- ); -}; -chartSize.story = { - name: 'chart size', -}; - -export const marginsAndPaddings = () => { - const theme: PartialTheme = { - chartMargins: { - left: range('margin left', 0, 50, 10), - right: range('margin right', 0, 50, 10), - top: range('margin top', 0, 50, 10), - bottom: range('margin bottom', 0, 50, 10), - }, - chartPaddings: { - left: range('padding left', 0, 50, 10), - right: range('padding right', 0, 50, 10), - top: range('padding top', 0, 50, 10), - bottom: range('padding bottom', 0, 50, 10), - }, - scales: { - barsPadding: range('bar padding', 0, 1, 0.1, undefined, 0.01), - }, - }; - const withLeftTitle = boolean('left axis with title', true); - const withBottomTitle = boolean('bottom axis with title', true); - const withRightTitle = boolean('right axis with title', true); - const withTopTitle = boolean('top axis with title', true); - return ( - - - - Number(d).toFixed(2)} - showGridLines={boolean('show left axis grid lines', false)} - /> - - Number(d).toFixed(2)} - showGridLines={boolean('show right axis grid lines', false)} - /> - - - ); -}; -marginsAndPaddings.story = { - name: 'margins and paddings', -}; - -export const axis = () => { - const theme: PartialTheme = { - axes: { - axisTitleStyle: { - fill: color('titleFill', '#333', 'Axis Title'), - fontSize: range('titleFontSize', 0, 40, 12, 'Axis Title'), - fontStyle: 'bold', - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, - padding: range('titlePadding', 0, 40, 5, 'Axis Title'), - }, - axisLineStyle: { - stroke: color('axisLinecolor', '#333', 'Axis Line'), - strokeWidth: range('axisLineWidth', 0, 5, 1, 'Axis Line'), - }, - tickLabelStyle: { - fill: color('tickFill', '#333', 'Tick Label'), - fontSize: range('tickFontSize', 0, 40, 10, 'Tick Label'), - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, - fontStyle: 'normal', - padding: number('tickLabelPadding', 1, {}, 'Tick Label'), - }, - tickLineStyle: { - visible: boolean('showTicks', true, 'Tick Line'), - stroke: color('tickLineColor', '#333', 'Tick Line'), - strokeWidth: range('tickLineWidth', 0, 5, 1, 'Tick Line'), - }, - }, - }; - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -axis.story = { - name: 'axis', -}; - -export const themeStyle = () => { - const customizeLineStroke = boolean('customizeLineStroke', false, 'line'); - const customizePointStroke = boolean('customizeLinePointStroke', false, 'line'); - const customizeAreaFill = boolean('customizeAreaFill', false, 'area'); - const customizeAreaLineStroke = boolean('customizeAreaLineStroke', false, 'area'); - const customizeRectFill = boolean('customizeRectFill', false, 'bar'); - const theme: PartialTheme = { - chartMargins: { - left: range('margin left', 0, 50, 10, 'Margins'), - right: range('margin right', 0, 50, 10, 'Margins'), - top: range('margin top', 0, 50, 10, 'Margins'), - bottom: range('margin bottom', 0, 50, 10, 'Margins'), - }, - chartPaddings: { - left: range('padding left', 0, 50, 10, 'Paddings'), - right: range('padding right', 0, 50, 10, 'Paddings'), - top: range('padding top', 0, 50, 10, 'Paddings'), - bottom: range('padding bottom', 0, 50, 10, 'Paddings'), - }, - lineSeriesStyle: { - line: { - stroke: customizeLineStroke ? color('customLineStroke', 'red', 'line') : undefined, - strokeWidth: range('lineStrokeWidth', 0, 10, 1, 'line'), - visible: boolean('lineVisible', true, 'line'), - }, - point: { - visible: boolean('linePointVisible', true, 'line'), - radius: range('linePointRadius', 0, 20, 1, 'line', 0.5), - fill: color('linePointFill', 'white', 'line'), - stroke: customizePointStroke ? color('customLinePointStroke', 'red', 'line') : undefined, - strokeWidth: range('linePointStrokeWidth', 0, 20, 0.5, 'line'), - opacity: range('linePointOpacity', 0, 1, 1, 'line', 0.01), - }, - }, - areaSeriesStyle: { - area: { - fill: customizeAreaFill ? color('customAreaFill', 'red', 'area') : undefined, - visible: boolean('aAreaVisible', true, 'area'), - opacity: range('aAreaOpacity', 0, 1, 1, 'area'), - }, - line: { - stroke: customizeAreaLineStroke ? color('customAreaLineStroke', 'red', 'area') : undefined, - strokeWidth: range('aStrokeWidth', 0, 10, 1, 'area'), - visible: boolean('aLineVisible', true, 'area'), - }, - point: { - visible: boolean('aPointVisible', true, 'area'), - fill: color('aPointFill', 'white', 'area'), - radius: range('aPointRadius', 0, 20, 1, 'area'), - stroke: color('aPointStroke', 'white', 'area'), - strokeWidth: range('aPointStrokeWidth', 0, 20, 0.5, 'area'), - opacity: range('aPointOpacity', 0, 1, 0.01, 'area'), - }, - }, - barSeriesStyle: { - rect: { - fill: customizeRectFill ? color('recCustomFull', 'red', 'bar') : undefined, - opacity: range('rectOpacity', 0, 1, 0.5, 'bar', 0.1), - }, - rectBorder: { - stroke: color('bBorderStroke', 'white', 'bar'), - strokeWidth: range('bStrokeWidth', 0, 10, 1, 'bar'), - visible: boolean('bBorderVisible', true, 'bar'), - }, - }, - sharedStyle: { - default: { - opacity: range('sOpacity', 0, 1, 1, 'Shared', 0.05), - }, - highlighted: { - opacity: range('sHighlighted', 0, 1, 1, 'Shared', 0.05), - }, - unhighlighted: { - opacity: range('sUnhighlighted', 0, 1, 0.25, 'Shared', 0.05), - }, - }, - colors: { - vizColors: select( - 'vizColors', - { - colorBlind: palettes.echPaletteColorBlind.colors, - darkBackground: palettes.echPaletteForDarkBackground.colors, - lightBackground: palettes.echPaletteForLightBackground.colors, - forStatus: palettes.echPaletteForStatus.colors, - }, - palettes.echPaletteColorBlind.colors, - 'Colors', - ), - defaultVizColor: DEFAULT_MISSING_COLOR, - }, - }; - - const darkmode = boolean('darkmode', false, 'Colors'); - const className = darkmode ? 'story-chart-dark' : 'story-chart'; - switchTheme(darkmode ? 'dark' : 'light'); - - return ( - - - - Number(d).toFixed(2)} - /> - - Number(d).toFixed(2)} - /> - - - - - ); -}; -themeStyle.story = { - name: 'theme/style', -}; - -export const partialCustomTheme = () => { - const customPartialTheme: PartialTheme = { - barSeriesStyle: { - rectBorder: { - stroke: color('BarBorderStroke', 'white'), - visible: true, - }, - }, - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - Number(d).toFixed(2)} - /> - - - ); -}; -partialCustomTheme.story = { - name: 'partial custom theme', -}; - -export const partialCustomThemeWithBaseTheme = () => { - const customPartialTheme: PartialTheme = { - barSeriesStyle: { - rectBorder: { - stroke: color('BarBorderStroke', 'white'), - visible: true, - }, - }, - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - Number(d).toFixed(2)} - /> - - - ); -}; -partialCustomThemeWithBaseTheme.story = { - name: 'partial custom theme with baseTheme', -}; - -export const multipleCustomPartialThemes = () => { - const primaryTheme: PartialTheme = { - barSeriesStyle: { - rect: { - fill: color('bar fill - primary theme', 'red'), - }, - }, - }; - const secondaryTheme: PartialTheme = { - barSeriesStyle: { - rect: { - fill: color('bar fill - secondary theme', 'blue'), - opacity: range('bar opacity - secondary theme', 0.1, 1, 0.7, undefined, 0.1), - }, - }, - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - Number(d).toFixed(2)} - /> - - - ); -}; -multipleCustomPartialThemes.story = { - name: 'multiple custom partial themes', -}; -// { -// info: { -// text: 'Notice that the secondary theme bar fill has no effect as the primary value takes priority', -// }, -// }, -// ) - -export const customSeriesColorsViaColorsArray = () => { - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -customSeriesColorsViaColorsArray.story = { - name: 'custom series colors via colors array', -}; - -export const customSeriesColorsViaAccessorFunction = () => { - const barColor = color('barSeriesColor', '#000'); - const barSeriesColorAccessor: CustomSeriesColors = ({ specId, yAccessor, splitAccessors }) => { - if ( - specId === getSpecId('bars') && - yAccessor === 'y1' && - // eslint-disable-next-line react/prop-types - splitAccessors.get('g1') === 'cloudflare.com' && - // eslint-disable-next-line react/prop-types - splitAccessors.get('g2') === 'direct-cdn' - ) { - return barColor; - } - return null; - }; - - const lineColor = color('linelineSeriesColor', '#ff0'); - const lineSeriesColorAccessor: CustomSeriesColors = ({ specId, yAccessor, splitAccessors }) => { - // eslint-disable-next-line react/prop-types - if (specId === getSpecId('lines') && yAccessor === 'y1' && splitAccessors.size === 0) { - return lineColor; - } - return null; - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -customSeriesColorsViaAccessorFunction.story = { - name: 'custom series colors via accessor function', -}; - -export const customSeriesStylesBars = () => { - const applyBarStyle = boolean('apply bar style (bar 1 series)', true, 'Chart Global Theme'); - - const barSeriesStyle = { - rectBorder: { - stroke: color('border stroke', 'blue', 'Bar 1 Style'), - strokeWidth: range('border strokeWidth', 0, 5, 2, 'Bar 1 Style', 0.1), - visible: boolean('border visible', true, 'Bar 1 Style'), - }, - rect: { - fill: color('rect fill', '#22C61A', 'Bar 1 Style'), - opacity: range('rect opacity', 0, 1, 0.3, 'Bar 1 Style', 0.1), - }, - }; - - const theme = { - barSeriesStyle: { - rectBorder: { - stroke: color('theme border stroke', 'red', 'Chart Global Theme'), - strokeWidth: range('theme border strokeWidth', 0, 5, 2, 'Chart Global Theme', 0.1), - visible: boolean('theme border visible', true, 'Chart Global Theme'), - }, - rect: { - opacity: range('theme opacity ', 0, 1, 0.9, 'Chart Global Theme', 0.1), - }, - }, - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -customSeriesStylesBars.story = { - name: 'custom series styles: bars', -}; - -export const customSeriesStylesLines = () => { - const applyLineStyles = boolean('apply line series style', true, 'Chart Global Theme'); - const lineSeriesStyle1 = generateLineSeriesStyleKnobs('Line 1 style', 'line1', 'lime', 'green', 4, 10, 6); - const lineSeriesStyle2 = generateLineSeriesStyleKnobs('Line 2 style', 'line2', 'blue', 'violet', 2, 5, 4); - - const chartTheme = { - lineSeriesStyle: generateLineSeriesStyleKnobs('Chart Global Theme', 'chartTheme'), - }; - - const dataset1 = [ - { x: 0, y: 3 }, - { x: 1, y: 2 }, - { x: 2, y: 4 }, - { x: 3, y: 10 }, - ]; - const dataset2 = dataset1.map((datum) => ({ ...datum, y: datum.y - 1 })); - const dataset3 = dataset1.map((datum) => ({ ...datum, y: datum.y - 2 })); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -customSeriesStylesLines.story = { - name: 'custom series styles: lines', -}; - -export const customSeriesStylesArea = () => { - const applyLineStyles = boolean('apply line series style', true, 'Chart Global Theme'); - - const chartTheme = { - areaSeriesStyle: generateAreaSeriesStyleKnobs('Chart Global Theme', 'chartTheme'), - }; - - const dataset1 = [ - { x: 0, y: 3 }, - { x: 1, y: 6 }, - { x: 2, y: 4 }, - { x: 3, y: 10 }, - ]; - const dataset2 = dataset1.map((datum) => ({ ...datum, y: datum.y - 1 })); - const dataset3 = dataset1.map((datum) => ({ ...datum, y: datum.y - 2 })); - - const areaStyle1 = generateAreaSeriesStyleKnobs('Area 1 Style', 'area1', 'lime', 'green', 4, 10, 6, 'black'); - const areaStyle2 = generateAreaSeriesStyleKnobs('Area 2 Style', 'area2', 'blue', 'violet', 2, 5, 4, undefined, 'red'); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - ); -}; -customSeriesStylesArea.story = { - name: 'custom series styles: area', -}; - -export const addCustomSeriesName = () => { - const customSeriesNamingFn: SeriesNameFn = ({ yAccessor, splitAccessors }) => { - // eslint-disable-next-line react/prop-types - if (yAccessor === 'y1' && splitAccessors.get('g') === 'a') { - return 'Custom full series name'; - } - - return null; - }; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -addCustomSeriesName.story = { - name: 'Add custom series name', -}; - -export const customSeriesNamingConfig = () => { - const customSeriesNameOptions: SeriesNameConfigOptions = { - names: [ - { - // replace split accessor; - accessor: 'g', - value: 'a', - name: 'replace a(from g)', - }, - { - // replace y accessor; - accessor: 'y2', - name: 'replace y2', - }, - ], - delimiter: ' | ', - }; - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -customSeriesNamingConfig.story = { - name: 'Add custom series naming and delimeter', -}; - -export const addCustomSeriesNameFormatting = () => { - const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); - const data = [ - { x: 1, y: 3, percent: 0.5, time: start.plus({ month: 1 }).toMillis() }, - { x: 2, y: 6, percent: 0.5, time: start.plus({ month: 2 }).toMillis() }, - { x: 3, y: 20, percent: 0.5, time: start.plus({ month: 3 }).toMillis() }, - { x: 1, y: 9, percent: 0.7, time: start.plus({ month: 1 }).toMillis() }, - { x: 2, y: 13, percent: 0.7, time: start.plus({ month: 2 }).toMillis() }, - { x: 3, y: 14, percent: 0.7, time: start.plus({ month: 3 }).toMillis() }, - { x: 1, y: 15, percent: 0.1, time: start.plus({ month: 1 }).toMillis() }, - { x: 2, y: 18, percent: 1, time: start.plus({ month: 2 }).toMillis() }, - { x: 3, y: 7, percent: 1, time: start.plus({ month: 3 }).toMillis() }, - ]; - const customSeriesNamingFn: SeriesNameFn = ({ yAccessor, splitAccessors }, isTooltip) => - [ - ...[...splitAccessors.entries()].map(([key, value]) => { - if (key === 'time') { - // Format time group - if (isTooltip) { - // Format tooltip time to be longer - return moment(value).format('ll'); - } - - // Format legend to be shorter - return moment(value).format('M/YYYY'); - } - - if (key === 'percent') { - // Format percent group - return `${(value as number) * 100}%`; - } - - return value; - }), - // don't format yAccessor - yAccessor, - ].join(' - '); - - return ( - - - - Number(d).toFixed(2)} - /> - - - - ); -}; -addCustomSeriesNameFormatting.story = { - name: 'Add custom series name formatting (legend/tooltip) [time/date and percent]', -}; - -export const tickLabelPaddingBothPropAndTheme = () => { - const theme: PartialTheme = { - axes: { - tickLabelStyle: { - fill: color('tickFill', '#333', 'Tick Label'), - fontSize: range('tickFontSize', 0, 40, 10, 'Tick Label'), - fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, - fontStyle: 'normal', - padding: number('Tick Label Padding Theme', 1, {}, 'Tick Label'), - }, - }, - }; - const customStyle = { - tickLabelPadding: number('Tick Label Padding Axis Spec', 0), - }; - return ( - - - - Number(d).toFixed(2)} - /> - - - ); -}; -tickLabelPaddingBothPropAndTheme.story = { - name: 'tickLabelPadding both prop and theme', -}; - -export const styleAccessorOverrides = () => { - const hasThreshold = boolean('threshold', true); - const threshold = number('min threshold', 3); - const barStyle: RecursivePartial = { - rect: { - opacity: 0.5, - fill: 'red', - }, - }; - const pointStyle: RecursivePartial = { - fill: 'red', - radius: 10, - }; - const barStyleAccessor: BarStyleAccessor = (d, g) => - g.specId === getSpecId('bar') && d.y1! > threshold ? barStyle : null; - const pointStyleAccessor: PointStyleAccessor = (d, g) => - (g.specId === getSpecId('line') || g.specId === getSpecId('area')) && d.y1! > threshold ? pointStyle : null; - - return ( - - - - Number(d).toFixed(2)} - /> - - - - - - - - ); -}; -styleAccessorOverrides.story = { - name: 'Style Accessor Overrides', -}; diff --git a/stories/stylings/10_custom_bars.tsx b/stories/stylings/10_custom_bars.tsx new file mode 100644 index 0000000000..19e5e7b257 --- /dev/null +++ b/stories/stylings/10_custom_bars.tsx @@ -0,0 +1,78 @@ +import { boolean, color, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +export const example = () => { + const applyBarStyle = boolean('apply bar style (bar 1 series)', true, 'Chart Global Theme'); + + const barSeriesStyle = { + rectBorder: { + stroke: color('border stroke', 'blue', 'Bar 1 Style'), + strokeWidth: range('border strokeWidth', 0, 5, 2, 'Bar 1 Style', 0.1), + visible: boolean('border visible', true, 'Bar 1 Style'), + }, + rect: { + fill: color('rect fill', '#22C61A', 'Bar 1 Style'), + opacity: range('rect opacity', 0, 1, 0.3, 'Bar 1 Style', 0.1), + }, + }; + + const theme = { + barSeriesStyle: { + rectBorder: { + stroke: color('theme border stroke', 'red', 'Chart Global Theme'), + strokeWidth: range('theme border strokeWidth', 0, 5, 2, 'Chart Global Theme', 0.1), + visible: boolean('theme border visible', true, 'Chart Global Theme'), + }, + rect: { + opacity: range('theme opacity ', 0, 1, 0.9, 'Chart Global Theme', 0.1), + }, + }, + }; + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/stylings/11_custom_lines.tsx b/stories/stylings/11_custom_lines.tsx new file mode 100644 index 0000000000..83d5c5fcc7 --- /dev/null +++ b/stories/stylings/11_custom_lines.tsx @@ -0,0 +1,102 @@ +import { boolean, color, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, Chart, LineSeries, Position, ScaleType, Settings, LineSeriesStyle } from '../../src'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +function generateLineSeriesStyleKnobs( + groupName: string, + tag: string, + pointFill?: string, + pointStroke?: string, + pointStrokeWidth?: number, + pointRadius?: number, + lineStrokeWidth?: number, + lineStroke?: string, +): LineSeriesStyle { + return { + line: { + stroke: lineStroke ? color(`line.stroke (${tag})`, lineStroke, groupName) : undefined, + strokeWidth: range(`line.strokeWidth (${tag})`, 0, 10, lineStrokeWidth ? lineStrokeWidth : 1, groupName), + visible: boolean(`line.visible (${tag})`, true, groupName), + opacity: range(`line.opacity (${tag})`, 0, 1, 1, groupName, 0.01), + }, + point: { + visible: boolean(`point.visible (${tag})`, true, groupName), + radius: range(`point.radius (${tag})`, 0, 20, pointRadius ? pointRadius : 5, groupName, 0.5), + opacity: range(`point.opacity (${tag})`, 0, 1, 1, groupName, 0.01), + stroke: color(`point.stroke (${tag})`, pointStroke ? pointStroke : 'black', groupName), + fill: color(`point.fill (${tag})`, pointFill ? pointFill : 'lightgray', groupName), + strokeWidth: range(`point.strokeWidth (${tag})`, 0, 5, pointStrokeWidth ? pointStrokeWidth : 2, groupName, 0.01), + }, + }; +} + +export const example = () => { + const applyLineStyles = boolean('apply line series style', true, 'Chart Global Theme'); + const lineSeriesStyle1 = generateLineSeriesStyleKnobs('Line 1 style', 'line1', 'lime', 'green', 4, 10, 6); + const lineSeriesStyle2 = generateLineSeriesStyleKnobs('Line 2 style', 'line2', 'blue', 'violet', 2, 5, 4); + + const chartTheme = { + lineSeriesStyle: generateLineSeriesStyleKnobs('Chart Global Theme', 'chartTheme'), + }; + + const dataset1 = [ + { x: 0, y: 3 }, + { x: 1, y: 2 }, + { x: 2, y: 4 }, + { x: 3, y: 10 }, + ]; + const dataset2 = dataset1.map((datum) => ({ ...datum, y: datum.y - 1 })); + const dataset3 = dataset1.map((datum) => ({ ...datum, y: datum.y - 2 })); + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/stylings/12_custom_area.tsx b/stories/stylings/12_custom_area.tsx new file mode 100644 index 0000000000..108e0e1f58 --- /dev/null +++ b/stories/stylings/12_custom_area.tsx @@ -0,0 +1,133 @@ +import { boolean, color, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AreaSeries, Axis, Chart, Position, ScaleType, Settings, LineSeriesStyle } from '../../src'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +function generateLineSeriesStyleKnobs( + groupName: string, + tag: string, + pointFill?: string, + pointStroke?: string, + pointStrokeWidth?: number, + pointRadius?: number, + lineStrokeWidth?: number, + lineStroke?: string, +): LineSeriesStyle { + return { + line: { + stroke: lineStroke ? color(`line.stroke (${tag})`, lineStroke, groupName) : undefined, + strokeWidth: range(`line.strokeWidth (${tag})`, 0, 10, lineStrokeWidth ? lineStrokeWidth : 1, groupName), + visible: boolean(`line.visible (${tag})`, true, groupName), + opacity: range(`line.opacity (${tag})`, 0, 1, 1, groupName, 0.01), + }, + point: { + visible: boolean(`point.visible (${tag})`, true, groupName), + radius: range(`point.radius (${tag})`, 0, 20, pointRadius ? pointRadius : 5, groupName, 0.5), + opacity: range(`point.opacity (${tag})`, 0, 1, 1, groupName, 0.01), + stroke: color(`point.stroke (${tag})`, pointStroke ? pointStroke : 'black', groupName), + fill: color(`point.fill (${tag})`, pointFill ? pointFill : 'lightgray', groupName), + strokeWidth: range(`point.strokeWidth (${tag})`, 0, 5, pointStrokeWidth ? pointStrokeWidth : 2, groupName, 0.01), + }, + }; +} + +function generateAreaSeriesStyleKnobs( + groupName: string, + tag: string, + pointFill?: string, + pointStroke?: string, + pointStrokeWidth?: number, + pointRadius?: number, + lineStrokeWidth?: number, + lineStroke?: string, + areaFill?: string, +) { + return { + ...generateLineSeriesStyleKnobs( + groupName, + tag, + pointFill, + pointStroke, + pointStrokeWidth, + pointRadius, + lineStrokeWidth, + lineStroke, + ), + area: { + fill: areaFill ? color(`area.fill (${tag})`, areaFill, groupName) : undefined, + visible: boolean(`area.visible (${tag})`, true, groupName), + opacity: range(`area.opacity (${tag})`, 0, 1, 0.8, groupName, 0.01), + }, + }; +} + +export const example = () => { + const applyLineStyles = boolean('apply line series style', true, 'Chart Global Theme'); + + const chartTheme = { + areaSeriesStyle: generateAreaSeriesStyleKnobs('Chart Global Theme', 'chartTheme'), + }; + + const dataset1 = [ + { x: 0, y: 3 }, + { x: 1, y: 6 }, + { x: 2, y: 4 }, + { x: 3, y: 10 }, + ]; + const dataset2 = dataset1.map((datum) => ({ ...datum, y: datum.y - 1 })); + const dataset3 = dataset1.map((datum) => ({ ...datum, y: datum.y - 2 })); + + const areaStyle1 = generateAreaSeriesStyleKnobs('Area 1 Style', 'area1', 'lime', 'green', 4, 10, 6, 'black'); + const areaStyle2 = generateAreaSeriesStyleKnobs('Area 2 Style', 'area2', 'blue', 'violet', 2, 5, 4, undefined, 'red'); + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/stylings/13_custom_series_name.tsx b/stories/stylings/13_custom_series_name.tsx new file mode 100644 index 0000000000..b7de2225b0 --- /dev/null +++ b/stories/stylings/13_custom_series_name.tsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SeriesNameFn } from '../../src/chart_types/xy_chart/utils/specs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const customSeriesNamingFn: SeriesNameFn = ({ yAccessor, splitAccessors }) => { + // eslint-disable-next-line react/prop-types + if (yAccessor === 'y1' && splitAccessors.get('g') === 'a') { + return 'Custom full series name'; + } + + return null; + }; + + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/stylings/13_custom_series_name_config.tsx b/stories/stylings/13_custom_series_name_config.tsx new file mode 100644 index 0000000000..4fa80dd5b8 --- /dev/null +++ b/stories/stylings/13_custom_series_name_config.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SeriesNameConfigOptions } from '../../src/chart_types/xy_chart/utils/specs'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const customSeriesNameOptions: SeriesNameConfigOptions = { + names: [ + { + // replace split accessor; + accessor: 'g', + value: 'a', + name: 'replace a(from g)', + }, + { + // replace y accessor; + accessor: 'y2', + name: 'replace y2', + }, + ], + delimiter: ' | ', + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/stylings/14_custom_series_name_formatting.tsx b/stories/stylings/14_custom_series_name_formatting.tsx new file mode 100644 index 0000000000..cd6b33ddb4 --- /dev/null +++ b/stories/stylings/14_custom_series_name_formatting.tsx @@ -0,0 +1,72 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import { SeriesNameFn } from '../../src/chart_types/xy_chart/utils/specs'; +import moment from 'moment'; +import { DateTime } from 'luxon'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); + const data = [ + { x: 1, y: 3, percent: 0.5, time: start.plus({ month: 1 }).toMillis() }, + { x: 2, y: 6, percent: 0.5, time: start.plus({ month: 2 }).toMillis() }, + { x: 3, y: 20, percent: 0.5, time: start.plus({ month: 3 }).toMillis() }, + { x: 1, y: 9, percent: 0.7, time: start.plus({ month: 1 }).toMillis() }, + { x: 2, y: 13, percent: 0.7, time: start.plus({ month: 2 }).toMillis() }, + { x: 3, y: 14, percent: 0.7, time: start.plus({ month: 3 }).toMillis() }, + { x: 1, y: 15, percent: 0.1, time: start.plus({ month: 1 }).toMillis() }, + { x: 2, y: 18, percent: 1, time: start.plus({ month: 2 }).toMillis() }, + { x: 3, y: 7, percent: 1, time: start.plus({ month: 3 }).toMillis() }, + ]; + const customSeriesNamingFn: SeriesNameFn = ({ yAccessor, splitAccessors }, isTooltip) => + [ + ...[...splitAccessors.entries()].map(([key, value]) => { + if (key === 'time') { + // Format time group + if (isTooltip) { + // Format tooltip time to be longer + return moment(value).format('ll'); + } + + // Format legend to be shorter + return moment(value).format('M/YYYY'); + } + + if (key === 'percent') { + // Format percent group + return `${(value as number) * 100}%`; + } + + return value; + }), + // don't format yAccessor + yAccessor, + ].join(' - '); + + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/stylings/15_tick_label.tsx b/stories/stylings/15_tick_label.tsx new file mode 100644 index 0000000000..8bd5380c5f --- /dev/null +++ b/stories/stylings/15_tick_label.tsx @@ -0,0 +1,61 @@ +import { boolean, color, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { AreaSeries, Axis, Chart, PartialTheme, Position, ScaleType, Settings } from '../../src'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +export const example = () => { + const theme: PartialTheme = { + axes: { + tickLabelStyle: { + fill: color('tickFill', '#333', 'Tick Label'), + fontSize: range('tickFontSize', 0, 40, 10, 'Tick Label'), + fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontStyle: 'normal', + padding: number('Tick Label Padding Theme', 1, {}, 'Tick Label'), + }, + }, + }; + const customStyle = { + tickLabelPadding: number('Tick Label Padding Axis Spec', 0), + }; + return ( + + + + Number(d).toFixed(2)} /> + + + ); +}; diff --git a/stories/stylings/16_style_accessor.tsx b/stories/stylings/16_style_accessor.tsx new file mode 100644 index 0000000000..4b02932e3d --- /dev/null +++ b/stories/stylings/16_style_accessor.tsx @@ -0,0 +1,96 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { + AreaSeries, + Axis, + BarSeries, + Chart, + LineSeries, + Position, + ScaleType, + Settings, + RecursivePartial, + BarSeriesStyle, + PointStyle, +} from '../../src'; +import { BarStyleAccessor, PointStyleAccessor } from '../../src/chart_types/xy_chart/utils/specs'; + +export const example = () => { + const hasThreshold = boolean('threshold', true); + const threshold = number('min threshold', 3); + const barStyle: RecursivePartial = { + rect: { + opacity: 0.5, + fill: 'red', + }, + }; + const pointStyle: RecursivePartial = { + fill: 'red', + radius: 10, + }; + const barStyleAccessor: BarStyleAccessor = (d, g) => (g.specId === 'bar' && d.y1! > threshold ? barStyle : null); + const pointStyleAccessor: PointStyleAccessor = (d, g) => + (g.specId === 'line' || g.specId === 'area') && d.y1! > threshold ? pointStyle : null; + + return ( + + + + Number(d).toFixed(2)} /> + + + + + + + + ); +}; diff --git a/stories/stylings/1_chart_size.tsx b/stories/stylings/1_chart_size.tsx new file mode 100644 index 0000000000..7748ed7baa --- /dev/null +++ b/stories/stylings/1_chart_size.tsx @@ -0,0 +1,85 @@ +import React from 'react'; + +import { BarSeries, Chart, ScaleType, Settings, TooltipType, RecursivePartial, Theme } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +const dg = new SeededDataGenerator(); +const data2 = dg.generateSimpleSeries(40); + +export const example = () => { + const theme: RecursivePartial = { + chartMargins: { + bottom: 0, + left: 0, + top: 0, + right: 0, + }, + }; + return ( +
+ + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/stylings/2_margins.tsx b/stories/stylings/2_margins.tsx new file mode 100644 index 0000000000..cdd43d91fe --- /dev/null +++ b/stories/stylings/2_margins.tsx @@ -0,0 +1,94 @@ +import { boolean, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings } from '../../src'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +export const example = () => { + const theme: PartialTheme = { + chartMargins: { + left: range('margin left', 0, 50, 10), + right: range('margin right', 0, 50, 10), + top: range('margin top', 0, 50, 10), + bottom: range('margin bottom', 0, 50, 10), + }, + chartPaddings: { + left: range('padding left', 0, 50, 10), + right: range('padding right', 0, 50, 10), + top: range('padding top', 0, 50, 10), + bottom: range('padding bottom', 0, 50, 10), + }, + scales: { + barsPadding: range('bar padding', 0, 1, 0.1, undefined, 0.01), + }, + }; + const withLeftTitle = boolean('left axis with title', true); + const withBottomTitle = boolean('bottom axis with title', true); + const withRightTitle = boolean('right axis with title', true); + const withTopTitle = boolean('top axis with title', true); + return ( + + + + Number(d).toFixed(2)} + showGridLines={boolean('show left axis grid lines', false)} + /> + + Number(d).toFixed(2)} + showGridLines={boolean('show right axis grid lines', false)} + /> + + + ); +}; diff --git a/stories/stylings/3_axis.tsx b/stories/stylings/3_axis.tsx new file mode 100644 index 0000000000..dae55aa7fe --- /dev/null +++ b/stories/stylings/3_axis.tsx @@ -0,0 +1,73 @@ +import { boolean, color, number, select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings } from '../../src'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +export const example = () => { + const theme: PartialTheme = { + axes: { + axisTitleStyle: { + fill: color('titleFill', '#333', 'Axis Title'), + fontSize: range('titleFontSize', 0, 40, 12, 'Axis Title'), + fontStyle: 'bold', + fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + padding: range('titlePadding', 0, 40, 5, 'Axis Title'), + }, + axisLineStyle: { + stroke: color('axisLinecolor', '#333', 'Axis Line'), + strokeWidth: range('axisLineWidth', 0, 5, 1, 'Axis Line'), + }, + tickLabelStyle: { + fill: color('tickFill', '#333', 'Tick Label'), + fontSize: range('tickFontSize', 0, 40, 10, 'Tick Label'), + fontFamily: `'Open Sans', Helvetica, Arial, sans-serif`, + fontStyle: 'normal', + padding: number('tickLabelPadding', 1, {}, 'Tick Label'), + }, + tickLineStyle: { + visible: boolean('showTicks', true, 'Tick Line'), + stroke: color('tickLineColor', '#333', 'Tick Line'), + strokeWidth: range('tickLineWidth', 0, 5, 1, 'Tick Line'), + }, + }, + }; + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; diff --git a/stories/stylings/4_theme_styling.tsx b/stories/stylings/4_theme_styling.tsx new file mode 100644 index 0000000000..3687ef7bdb --- /dev/null +++ b/stories/stylings/4_theme_styling.tsx @@ -0,0 +1,182 @@ +import { boolean, color, number, select } from '@storybook/addon-knobs'; +import React from 'react'; + +import { switchTheme } from '../../.storybook/theme_service'; +import { + AreaSeries, + Axis, + BarSeries, + Chart, + CurveType, + DEFAULT_MISSING_COLOR, + LineSeries, + PartialTheme, + Position, + ScaleType, + Settings, + LIGHT_THEME, + DARK_THEME, +} from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; +import { palettes } from '../../src/utils/themes/colors'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +const dg = new SeededDataGenerator(); +const data1 = dg.generateGroupedSeries(40, 4); +const data2 = dg.generateSimpleSeries(40); +const data3 = dg.generateSimpleSeries(40); + +export const example = () => { + const customizeLineStroke = boolean('customizeLineStroke', false, 'line'); + const customizePointStroke = boolean('customizeLinePointStroke', false, 'line'); + const customizeAreaFill = boolean('customizeAreaFill', false, 'area'); + const customizeAreaLineStroke = boolean('customizeAreaLineStroke', false, 'area'); + const customizeRectFill = boolean('customizeRectFill', false, 'bar'); + const theme: PartialTheme = { + chartMargins: { + left: range('margin left', 0, 50, 10, 'Margins'), + right: range('margin right', 0, 50, 10, 'Margins'), + top: range('margin top', 0, 50, 10, 'Margins'), + bottom: range('margin bottom', 0, 50, 10, 'Margins'), + }, + chartPaddings: { + left: range('padding left', 0, 50, 10, 'Paddings'), + right: range('padding right', 0, 50, 10, 'Paddings'), + top: range('padding top', 0, 50, 10, 'Paddings'), + bottom: range('padding bottom', 0, 50, 10, 'Paddings'), + }, + lineSeriesStyle: { + line: { + stroke: customizeLineStroke ? color('customLineStroke', 'red', 'line') : undefined, + strokeWidth: range('lineStrokeWidth', 0, 10, 1, 'line'), + visible: boolean('lineVisible', true, 'line'), + }, + point: { + visible: boolean('linePointVisible', true, 'line'), + radius: range('linePointRadius', 0, 20, 1, 'line', 0.5), + fill: color('linePointFill', 'white', 'line'), + stroke: customizePointStroke ? color('customLinePointStroke', 'red', 'line') : undefined, + strokeWidth: range('linePointStrokeWidth', 0, 20, 0.5, 'line'), + opacity: range('linePointOpacity', 0, 1, 1, 'line', 0.01), + }, + }, + areaSeriesStyle: { + area: { + fill: customizeAreaFill ? color('customAreaFill', 'red', 'area') : undefined, + visible: boolean('aAreaVisible', true, 'area'), + opacity: range('aAreaOpacity', 0, 1, 1, 'area'), + }, + line: { + stroke: customizeAreaLineStroke ? color('customAreaLineStroke', 'red', 'area') : undefined, + strokeWidth: range('aStrokeWidth', 0, 10, 1, 'area'), + visible: boolean('aLineVisible', true, 'area'), + }, + point: { + visible: boolean('aPointVisible', true, 'area'), + fill: color('aPointFill', 'white', 'area'), + radius: range('aPointRadius', 0, 20, 1, 'area'), + stroke: color('aPointStroke', 'white', 'area'), + strokeWidth: range('aPointStrokeWidth', 0, 20, 0.5, 'area'), + opacity: range('aPointOpacity', 0, 1, 0.01, 'area'), + }, + }, + barSeriesStyle: { + rect: { + fill: customizeRectFill ? color('recCustomFull', 'red', 'bar') : undefined, + opacity: range('rectOpacity', 0, 1, 0.5, 'bar', 0.1), + }, + rectBorder: { + stroke: color('bBorderStroke', 'white', 'bar'), + strokeWidth: range('bStrokeWidth', 0, 10, 1, 'bar'), + visible: boolean('bBorderVisible', true, 'bar'), + }, + }, + sharedStyle: { + default: { + opacity: range('sOpacity', 0, 1, 1, 'Shared', 0.05), + }, + highlighted: { + opacity: range('sHighlighted', 0, 1, 1, 'Shared', 0.05), + }, + unhighlighted: { + opacity: range('sUnhighlighted', 0, 1, 0.25, 'Shared', 0.05), + }, + }, + colors: { + vizColors: select( + 'vizColors', + { + colorBlind: palettes.echPaletteColorBlind.colors, + darkBackground: palettes.echPaletteForDarkBackground.colors, + lightBackground: palettes.echPaletteForLightBackground.colors, + forStatus: palettes.echPaletteForStatus.colors, + }, + palettes.echPaletteColorBlind.colors, + 'Colors', + ), + defaultVizColor: DEFAULT_MISSING_COLOR, + }, + }; + + const darkmode = boolean('darkmode', false, 'Colors'); + const className = darkmode ? 'story-chart-dark' : 'story-chart'; + switchTheme(darkmode ? 'dark' : 'light'); + + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/stylings/5_partial_custom_theme.tsx b/stories/stylings/5_partial_custom_theme.tsx new file mode 100644 index 0000000000..244c57d602 --- /dev/null +++ b/stories/stylings/5_partial_custom_theme.tsx @@ -0,0 +1,39 @@ +import { color } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; + +const dg = new SeededDataGenerator(); +const data1 = dg.generateGroupedSeries(40, 4); + +export const example = () => { + const customPartialTheme: PartialTheme = { + barSeriesStyle: { + rectBorder: { + stroke: color('BarBorderStroke', 'white'), + visible: true, + }, + }, + }; + + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + ); +}; diff --git a/stories/stylings/6_partial_and_base.tsx b/stories/stylings/6_partial_and_base.tsx new file mode 100644 index 0000000000..cb97ee8198 --- /dev/null +++ b/stories/stylings/6_partial_and_base.tsx @@ -0,0 +1,45 @@ +import { color } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings, LIGHT_THEME } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; + +const dg = new SeededDataGenerator(); +const data1 = dg.generateGroupedSeries(40, 4); + +export const example = () => { + const customPartialTheme: PartialTheme = { + barSeriesStyle: { + rectBorder: { + stroke: color('BarBorderStroke', 'white'), + visible: true, + }, + }, + }; + + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + ); +}; diff --git a/stories/stylings/7_multiple_custom.tsx b/stories/stylings/7_multiple_custom.tsx new file mode 100644 index 0000000000..77b0c6e999 --- /dev/null +++ b/stories/stylings/7_multiple_custom.tsx @@ -0,0 +1,60 @@ +import { color, number } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings } from '../../src'; +import { SeededDataGenerator } from '../../src/mocks/utils'; + +function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { + return number( + title, + value, + { + range: true, + min, + max, + step, + }, + groupId, + ); +} + +const dg = new SeededDataGenerator(); +const data1 = dg.generateGroupedSeries(40, 4); + +export const example = () => { + const primaryTheme: PartialTheme = { + barSeriesStyle: { + rect: { + fill: color('bar fill - primary theme', 'red'), + }, + }, + }; + const secondaryTheme: PartialTheme = { + barSeriesStyle: { + rect: { + fill: color('bar fill - secondary theme', 'blue'), + opacity: range('bar opacity - secondary theme', 0.1, 1, 0.7, undefined, 0.1), + }, + }, + }; + + return ( + + + + Number(d).toFixed(2)} /> + + Number(d).toFixed(2)} /> + + + ); +}; diff --git a/stories/stylings/8_custom_series_colors_array.tsx b/stories/stylings/8_custom_series_colors_array.tsx new file mode 100644 index 0000000000..c52ca08e46 --- /dev/null +++ b/stories/stylings/8_custom_series_colors_array.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export const example = () => { + return ( + + + + Number(d).toFixed(2)} /> + + + + ); +}; + +// storybook configuration +example.story = { + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; diff --git a/stories/stylings/9_custom_series_colors_function.tsx b/stories/stylings/9_custom_series_colors_function.tsx new file mode 100644 index 0000000000..54b38a274c --- /dev/null +++ b/stories/stylings/9_custom_series_colors_function.tsx @@ -0,0 +1,64 @@ +import { color } from '@storybook/addon-knobs'; +import React from 'react'; + +import { Axis, BarSeries, Chart, CustomSeriesColors, LineSeries, Position, ScaleType, Settings } from '../../src'; +import * as TestDatasets from '../../src/utils/data_samples/test_dataset'; + +export const example = () => { + const barColor = color('barSeriesColor', '#000'); + const barSeriesColorAccessor: CustomSeriesColors = ({ specId, yAccessor, splitAccessors }) => { + if ( + specId === 'bars' && + yAccessor === 'y1' && + // eslint-disable-next-line react/prop-types + splitAccessors.get('g1') === 'cloudflare.com' && + // eslint-disable-next-line react/prop-types + splitAccessors.get('g2') === 'direct-cdn' + ) { + return barColor; + } + return null; + }; + + const lineColor = color('linelineSeriesColor', '#ff0'); + const lineSeriesColorAccessor: CustomSeriesColors = ({ specId, yAccessor, splitAccessors }) => { + // eslint-disable-next-line react/prop-types + if (specId === 'lines' && yAccessor === 'y1' && splitAccessors.size === 0) { + return lineColor; + } + return null; + }; + + return ( + + + + Number(d).toFixed(2)} /> + + + + + ); +}; diff --git a/stories/stylings/stylings.stories.tsx b/stories/stylings/stylings.stories.tsx new file mode 100644 index 0000000000..38dfa32836 --- /dev/null +++ b/stories/stylings/stylings.stories.tsx @@ -0,0 +1,27 @@ +import { SB_KNOBS_PANEL } from '../utils/storybook'; + +export default { + title: 'Stylings', + parameters: { + options: { selectedPanel: SB_KNOBS_PANEL }, + }, +}; + +export { example as chartSize } from './1_chart_size'; +export { example as marginsAndPaddings } from './2_margins'; +export { example as axis } from './3_axis'; +export { example as themeStyling } from './4_theme_styling'; +export { example as partialCustomTheme } from './5_partial_custom_theme'; +export { example as partialCustomThemeWithBaseTheme } from './6_partial_and_base'; +export { example as multipleCustomPartialThemes } from './7_multiple_custom'; +export { example as customSeriesColorsViaColorsArray } from './8_custom_series_colors_array'; +export { example as customSeriesColorsViaAccessorFunction } from './9_custom_series_colors_function'; + +export { example as customSeriesStylesBars } from './10_custom_bars'; +export { example as customSeriesStylesLines } from './11_custom_lines'; +export { example as customSeriesStylesArea } from './12_custom_area'; +export { example as customSeriesName } from './13_custom_series_name'; +export { example as customSeriesNameConfig } from './13_custom_series_name_config'; +export { example as customSeriesNameFormatting } from './14_custom_series_name_formatting'; +export { example as tickLabelPaddingBothPropAndTheme } from './15_tick_label'; +export { example as styleAccessorOverrides } from './16_style_accessor'; diff --git a/stories/sunburst.tsx b/stories/sunburst.tsx deleted file mode 100644 index 4a6fc19363..0000000000 --- a/stories/sunburst.tsx +++ /dev/null @@ -1,833 +0,0 @@ -import { Chart, Datum, Partition, PartitionLayout } from '../src'; -import { mocks } from '../src/mocks/hierarchical/index'; -import { config } from '../src/chart_types/partition_chart/layout/config/config'; -import React from 'react'; -import { ShapeTreeNode } from '../src/chart_types/partition_chart/layout/types/viewmodel_types'; -import { - categoricalFillColor, - colorBrewerCategoricalPastel12, - colorBrewerCategoricalStark9, - countryLookup, - indexInterpolatedFillColor, - interpolatorCET2s, - interpolatorTurbo, - productLookup, - regionLookup, -} from './utils/utils'; - -export default { - title: 'Sunburst', - parameters: { - info: { - source: false, - }, - }, -}; - -export const SimplePieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - /> - -); -SimplePieChart.story = { - name: 'Most basic pie chart', - info: { - source: false, - }, -}; - -export const ValueFormattedPieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { - textInvertible: true, - fontWeight: 100, - fontStyle: 'italic', - valueFont: { - fontFamily: 'Menlo', - fontStyle: 'normal', - fontWeight: 900, - }, - }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorTurbo), - }, - }, - ]} - config={{ outerSizeRatio: 0.9 }} - /> - -); -ValueFormattedPieChart.story = { - name: 'Value formatted pie chart', - info: { - source: false, - }, -}; - -export const ValueFormattedPieChart2 = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { - textInvertible: true, - fontWeight: 100, - fontStyle: 'italic', - valueFont: { - fontFamily: 'Menlo', - fontStyle: 'normal', - fontWeight: 900, - }, - }, - shape: { - fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex), - }, - }, - ]} - config={{ outerSizeRatio: 0.9 }} - /> - -); -ValueFormattedPieChart2.story = { - name: 'Value formatted pie chart with categorical color palette', - info: { - source: false, - }, -}; - -export const PieChartWithFillLabels = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { - maxCount: 32, - fontSize: 14, - }, - fontFamily: 'Arial', - fillLabel: { - valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontStyle: 'italic', - }, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 1, - idealFontSizeJump: 1.1, - outerSizeRatio: 0.9, // - 0.5 * Math.random(), - emptySizeRatio: 0, - circlePadding: 4, - backgroundColor: 'rgba(229,229,229,1)', - }} - /> - -); - -PieChartWithFillLabels.story = { - name: 'Pie chart with fill labels', -}; - -export const DonutChartWithFillLabels = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { - maxCount: 32, - fontSize: 14, - }, - fontFamily: 'Arial', - fillLabel: { - valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontStyle: 'italic', - }, - margin: { top: 0, bottom: 0, left: 0.2, right: 0 }, - minFontSize: 1, - idealFontSizeJump: 1.1, - outerSizeRatio: 0.9, // - 0.5 * Math.random(), - emptySizeRatio: 0.4, - circlePadding: 4, - backgroundColor: 'rgba(229,229,229,1)', - }} - /> - -); -DonutChartWithFillLabels.story = { - name: 'Donut chart with fill labels', -}; - -export const PieChartLabels = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - // nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -PieChartLabels.story = { - name: 'Pie chart with direct text labels instead of dimensions lookup', -}; - -export const SomeZeroValueSlice = () => ( - - ({ ...s, exportVal: 0 }))) - .concat(mocks.pie.slice(4))} - valueAccessor={(d: Datum) => d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -SomeZeroValueSlice.story = { - name: 'Some slices have a zero value', -}; - -export const SunburstTwoLayers = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: (d: any) => regionLookup[d].regionName, - fillLabel: { - fontFamily: 'Impact', - valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000000))}\xa0Tn`, - }, - shape: { - fillColor: (d) => { - // pick color from color palette based on mean angle - rather distinct colors in the inner ring - return indexInterpolatedFillColor(interpolatorCET2s)(d, (d.x0 + d.x1) / 2 / (2 * Math.PI), []); - }, - }, - }, - { - groupByRollup: (d: Datum) => d.dest, - nodeLabel: (d: any) => countryLookup[d].name, - shape: { - fillColor: (d) => { - // pick color from color palette based on mean angle - related yet distinct colors in the outer ring - return indexInterpolatedFillColor(interpolatorCET2s)(d, (d.x0 + d.x1) / 2 / (2 * Math.PI), []); - }, - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { - maxCount: 0, - fontSize: 14, - }, - fontFamily: 'Arial', - fillLabel: { - textInvertible: true, - valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontStyle: 'italic', - }, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 1, - idealFontSizeJump: 1.1, - outerSizeRatio: 0.95, - emptySizeRatio: 0, - circlePadding: 4, - backgroundColor: 'rgba(229,229,229,1)', - }} - /> - -); -SunburstTwoLayers.story = { - name: 'Sunburst with two layers, angle color', -}; - -export const SunburstThreeLayers = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: any) => productLookup[d].name, - shape: { - fillColor: (d: ShapeTreeNode) => { - return categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex); - }, - }, - }, - { - groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: (d: any) => regionLookup[d].regionName, - shape: { - fillColor: (d: ShapeTreeNode) => { - return categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex); - }, - }, - }, - { - groupByRollup: (d: Datum) => d.dest, - nodeLabel: (d: any) => countryLookup[d].name, - shape: { - fillColor: (d: ShapeTreeNode) => { - return categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex); - }, - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { - maxCount: 0, - fontSize: 14, - }, - fontFamily: 'Arial', - fillLabel: { - valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontStyle: 'italic', - textInvertible: true, - fontWeight: 900, - valueFont: { - fontFamily: 'Menlo', - fontStyle: 'normal', - fontWeight: 100, - }, - }, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 1, - idealFontSizeJump: 1.1, - outerSizeRatio: 1, - emptySizeRatio: 0, - circlePadding: 4, - backgroundColor: 'rgba(229,229,229,1)', - }} - /> - -); -SunburstThreeLayers.story = { - name: 'Sunburst with three layers, ColorBrewer, fade', -}; - -export const TwoSlicesPieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -TwoSlicesPieChart.story = { - name: 'Pie chart with two slices', -}; - -export const LargeSmallPieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - clockwiseSectors: true, - specialFirstInnermostSector: false, - outerSizeRatio: 1, - }} - /> - -); -LargeSmallPieChart.story = { - name: 'Pie chart with one large and one small slice', -}; - -export const VeryLargeSmallPieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => d, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -VeryLargeSmallPieChart.story = { - name: 'Pie chart with one very large and one very small slice', -}; - -export const BigEmptyPieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -BigEmptyPieChart.story = { - name: 'Pie chart with one near-full and one near-zero slice', -}; - -export const FullZeroSlicePieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -FullZeroSlicePieChart.story = { - name: 'Pie chart with one full and one zero slice', -}; - -export const SingleSlicePieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -SingleSlicePieChart.story = { - name: 'Pie chart with a single slice', -}; - -export const SingleSmallSlicePieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst, outerSizeRatio: 0.15 }} - /> - -); -SingleSmallSlicePieChart.story = { - name: 'Small pie chart with a single slice', -}; - -export const SingleVerySmallSlicePieChart = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst, outerSizeRatio: 0.03 }} - /> - -); -SingleVerySmallSlicePieChart.story = { - name: 'Very small pie chart with a single slice', -}; - -export const NoSliceNoPie = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -NoSliceNoPie.story = { - name: 'No pie chart if no slices', -}; - -export const NegativeNoPie = () => ( - - ({ ...s, exportVal: -0.1 }))) - .concat(mocks.pie.slice(3))} - valueAccessor={(d: Datum) => d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -NegativeNoPie.story = { - name: 'No pie chart if some slices are negative', -}; - -export const TotalZeroNoPie = () => ( - - ({ ...s, exportVal: 0 }))} - valueAccessor={(d: Datum) => d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ partitionLayout: PartitionLayout.sunburst }} - /> - -); -TotalZeroNoPie.story = { - name: 'No pie chart if total is zero', -}; - -export const HighNumberOfSlice = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.origin, - nodeLabel: (d: Datum) => countryLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { maxCount: 15 }, - }} - /> - -); -HighNumberOfSlice.story = { - name: 'Hundreds of slices, vanishing & tapering borders', -}; - -export const CounterClockwiseSpecial = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - clockwiseSectors: false, - }} - /> - -); -CounterClockwiseSpecial.story = { - name: 'Counterclockwise, special 1st', -}; - -export const ClockwiseNoSpecial = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - specialFirstInnermostSector: false, - }} - /> - -); -ClockwiseNoSpecial.story = { - name: 'Clockwise, non-special 1st', -}; - -export const LinkedLabelsOnly = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { maximumSection: Infinity }, - }} - /> - -); -LinkedLabelsOnly.story = { - name: 'Linked labels only', -}; - -export const NoLabels = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { textInvertible: true }, - shape: { - fillColor: indexInterpolatedFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.sunburst, - linkLabel: { maximumSection: Infinity, maxCount: 0 }, - }} - /> - -); -NoLabels.story = { - name: 'No labels at all', -}; diff --git a/stories/sunburst/10_2_slice.tsx b/stories/sunburst/10_2_slice.tsx new file mode 100644 index 0000000000..f69f0eba98 --- /dev/null +++ b/stories/sunburst/10_2_slice.tsx @@ -0,0 +1,27 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/11_small_large.tsx b/stories/sunburst/11_small_large.tsx new file mode 100644 index 0000000000..191188ecc1 --- /dev/null +++ b/stories/sunburst/11_small_large.tsx @@ -0,0 +1,34 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => d, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + clockwiseSectors: true, + specialFirstInnermostSector: false, + outerSizeRatio: 1, + }} + /> + +); diff --git a/stories/sunburst/12_very_small.tsx b/stories/sunburst/12_very_small.tsx new file mode 100644 index 0000000000..39929dcd0c --- /dev/null +++ b/stories/sunburst/12_very_small.tsx @@ -0,0 +1,29 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => d, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/13_empty.tsx b/stories/sunburst/13_empty.tsx new file mode 100644 index 0000000000..05be96bb66 --- /dev/null +++ b/stories/sunburst/13_empty.tsx @@ -0,0 +1,29 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/14_full_zero.tsx b/stories/sunburst/14_full_zero.tsx new file mode 100644 index 0000000000..c7c70591c8 --- /dev/null +++ b/stories/sunburst/14_full_zero.tsx @@ -0,0 +1,29 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/15_single.tsx b/stories/sunburst/15_single.tsx new file mode 100644 index 0000000000..0a4d6c2b20 --- /dev/null +++ b/stories/sunburst/15_single.tsx @@ -0,0 +1,27 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/16_single_small.tsx b/stories/sunburst/16_single_small.tsx new file mode 100644 index 0000000000..7891bb601a --- /dev/null +++ b/stories/sunburst/16_single_small.tsx @@ -0,0 +1,27 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst, outerSizeRatio: 0.15 }} + /> + +); diff --git a/stories/sunburst/17_single_very_small.tsx b/stories/sunburst/17_single_very_small.tsx new file mode 100644 index 0000000000..11dee44352 --- /dev/null +++ b/stories/sunburst/17_single_very_small.tsx @@ -0,0 +1,27 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst, outerSizeRatio: 0.03 }} + /> + +); diff --git a/stories/sunburst/18_no_sliced.tsx b/stories/sunburst/18_no_sliced.tsx new file mode 100644 index 0000000000..414d04f1c2 --- /dev/null +++ b/stories/sunburst/18_no_sliced.tsx @@ -0,0 +1,26 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/19_negative.tsx b/stories/sunburst/19_negative.tsx new file mode 100644 index 0000000000..f9e9efada9 --- /dev/null +++ b/stories/sunburst/19_negative.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + ({ ...s, exportVal: -0.1 }))) + .concat(mocks.pie.slice(3))} + valueAccessor={(d: Datum) => d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/1_simple.tsx b/stories/sunburst/1_simple.tsx new file mode 100644 index 0000000000..c930231698 --- /dev/null +++ b/stories/sunburst/1_simple.tsx @@ -0,0 +1,26 @@ +import { Chart, Datum, Partition } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + /> + +); diff --git a/stories/sunburst/20_total_zero.tsx b/stories/sunburst/20_total_zero.tsx new file mode 100644 index 0000000000..aceec1233d --- /dev/null +++ b/stories/sunburst/20_total_zero.tsx @@ -0,0 +1,27 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + ({ ...s, exportVal: 0 }))} + valueAccessor={(d: Datum) => d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/21_high_pie.tsx b/stories/sunburst/21_high_pie.tsx new file mode 100644 index 0000000000..72e8d14bce --- /dev/null +++ b/stories/sunburst/21_high_pie.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { countryLookup, indexInterpolatedFillColor, interpolatorCET2s } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.origin, + nodeLabel: (d: Datum) => countryLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { maxCount: 15 }, + }} + /> + +); diff --git a/stories/sunburst/22_counter_clockwise.tsx b/stories/sunburst/22_counter_clockwise.tsx new file mode 100644 index 0000000000..adfd9ac870 --- /dev/null +++ b/stories/sunburst/22_counter_clockwise.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + clockwiseSectors: false, + }} + /> + +); diff --git a/stories/sunburst/23_clockwise.tsx b/stories/sunburst/23_clockwise.tsx new file mode 100644 index 0000000000..0eed320e1e --- /dev/null +++ b/stories/sunburst/23_clockwise.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + specialFirstInnermostSector: false, + }} + /> + +); diff --git a/stories/sunburst/24_linked_label.tsx b/stories/sunburst/24_linked_label.tsx new file mode 100644 index 0000000000..13fcc30e51 --- /dev/null +++ b/stories/sunburst/24_linked_label.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { maximumSection: Infinity }, + }} + /> + +); diff --git a/stories/sunburst/25_no_labels.tsx b/stories/sunburst/25_no_labels.tsx new file mode 100644 index 0000000000..48809a5505 --- /dev/null +++ b/stories/sunburst/25_no_labels.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { maximumSection: Infinity, maxCount: 0 }, + }} + /> + +); diff --git a/stories/sunburst/2_value_formatted.tsx b/stories/sunburst/2_value_formatted.tsx new file mode 100644 index 0000000000..f7dbf8b560 --- /dev/null +++ b/stories/sunburst/2_value_formatted.tsx @@ -0,0 +1,36 @@ +import { Chart, Datum, Partition } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorTurbo, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { + textInvertible: true, + fontWeight: 100, + fontStyle: 'italic', + valueFont: { + fontFamily: 'Menlo', + fontStyle: 'normal', + fontWeight: 900, + }, + }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorTurbo), + }, + }, + ]} + config={{ outerSizeRatio: 0.9 }} + /> + +); diff --git a/stories/sunburst/3_value_formatted_2.tsx b/stories/sunburst/3_value_formatted_2.tsx new file mode 100644 index 0000000000..a1c1825503 --- /dev/null +++ b/stories/sunburst/3_value_formatted_2.tsx @@ -0,0 +1,37 @@ +import { Chart, Datum, Partition } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types'; +import { categoricalFillColor, colorBrewerCategoricalPastel12, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { + textInvertible: true, + fontWeight: 100, + fontStyle: 'italic', + valueFont: { + fontFamily: 'Menlo', + fontStyle: 'normal', + fontWeight: 900, + }, + }, + shape: { + fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex), + }, + }, + ]} + config={{ outerSizeRatio: 0.9 }} + /> + +); diff --git a/stories/sunburst/4_fill_labels.tsx b/stories/sunburst/4_fill_labels.tsx new file mode 100644 index 0000000000..3bbad94caf --- /dev/null +++ b/stories/sunburst/4_fill_labels.tsx @@ -0,0 +1,45 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { + maxCount: 32, + fontSize: 14, + }, + fontFamily: 'Arial', + fillLabel: { + valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + fontStyle: 'italic', + }, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 1, + idealFontSizeJump: 1.1, + outerSizeRatio: 0.9, // - 0.5 * Math.random(), + emptySizeRatio: 0, + circlePadding: 4, + backgroundColor: 'rgba(229,229,229,1)', + }} + /> + +); diff --git a/stories/sunburst/5_donut.tsx b/stories/sunburst/5_donut.tsx new file mode 100644 index 0000000000..e0a328230e --- /dev/null +++ b/stories/sunburst/5_donut.tsx @@ -0,0 +1,45 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { + maxCount: 32, + fontSize: 14, + }, + fontFamily: 'Arial', + fillLabel: { + valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + fontStyle: 'italic', + }, + margin: { top: 0, bottom: 0, left: 0.2, right: 0 }, + minFontSize: 1, + idealFontSizeJump: 1.1, + outerSizeRatio: 0.9, // - 0.5 * Math.random(), + emptySizeRatio: 0.4, + circlePadding: 4, + backgroundColor: 'rgba(229,229,229,1)', + }} + /> + +); diff --git a/stories/sunburst/6_pie_chart_labels.tsx b/stories/sunburst/6_pie_chart_labels.tsx new file mode 100644 index 0000000000..ab49c3d663 --- /dev/null +++ b/stories/sunburst/6_pie_chart_labels.tsx @@ -0,0 +1,29 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d))}`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + // nodeLabel: (d: Datum) => d, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/7_zero_slice.tsx b/stories/sunburst/7_zero_slice.tsx new file mode 100644 index 0000000000..4927154bc0 --- /dev/null +++ b/stories/sunburst/7_zero_slice.tsx @@ -0,0 +1,30 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '../utils/utils'; + +export const example = () => ( + + ({ ...s, exportVal: 0 }))) + .concat(mocks.pie.slice(4))} + valueAccessor={(d: Datum) => d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { textInvertible: true }, + shape: { + fillColor: indexInterpolatedFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ partitionLayout: PartitionLayout.sunburst }} + /> + +); diff --git a/stories/sunburst/8_sunburst_two_layers.tsx b/stories/sunburst/8_sunburst_two_layers.tsx new file mode 100644 index 0000000000..ff709ea3aa --- /dev/null +++ b/stories/sunburst/8_sunburst_two_layers.tsx @@ -0,0 +1,62 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { countryLookup, indexInterpolatedFillColor, interpolatorCET2s, regionLookup } from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), + nodeLabel: (d: any) => regionLookup[d].regionName, + fillLabel: { + fontFamily: 'Impact', + valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000000))}\xa0Tn`, + }, + shape: { + fillColor: (d) => { + // pick color from color palette based on mean angle - rather distinct colors in the inner ring + return indexInterpolatedFillColor(interpolatorCET2s)(d, (d.x0 + d.x1) / 2 / (2 * Math.PI), []); + }, + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + shape: { + fillColor: (d) => { + // pick color from color palette based on mean angle - related yet distinct colors in the outer ring + return indexInterpolatedFillColor(interpolatorCET2s)(d, (d.x0 + d.x1) / 2 / (2 * Math.PI), []); + }, + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { + maxCount: 0, + fontSize: 14, + }, + fontFamily: 'Arial', + fillLabel: { + textInvertible: true, + valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + fontStyle: 'italic', + }, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 1, + idealFontSizeJump: 1.1, + outerSizeRatio: 0.95, + emptySizeRatio: 0, + circlePadding: 4, + backgroundColor: 'rgba(229,229,229,1)', + }} + /> + +); diff --git a/stories/sunburst/9_sunburst_three_layers.tsx b/stories/sunburst/9_sunburst_three_layers.tsx new file mode 100644 index 0000000000..7fa671742b --- /dev/null +++ b/stories/sunburst/9_sunburst_three_layers.tsx @@ -0,0 +1,78 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import React from 'react'; +import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types'; +import { + categoricalFillColor, + colorBrewerCategoricalStark9, + countryLookup, + productLookup, + regionLookup, +} from '../utils/utils'; + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: any) => productLookup[d].name, + shape: { + fillColor: (d: ShapeTreeNode) => { + return categoricalFillColor(colorBrewerCategoricalStark9, 0.7)(d.sortIndex); + }, + }, + }, + { + groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), + nodeLabel: (d: any) => regionLookup[d].regionName, + shape: { + fillColor: (d: ShapeTreeNode) => { + return categoricalFillColor(colorBrewerCategoricalStark9, 0.5)(d.parent.sortIndex); + }, + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + shape: { + fillColor: (d: ShapeTreeNode) => { + return categoricalFillColor(colorBrewerCategoricalStark9, 0.3)(d.parent.parent.sortIndex); + }, + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.sunburst, + linkLabel: { + maxCount: 0, + fontSize: 14, + }, + fontFamily: 'Arial', + fillLabel: { + valueFormatter: (d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + fontStyle: 'italic', + textInvertible: true, + fontWeight: 900, + valueFont: { + fontFamily: 'Menlo', + fontStyle: 'normal', + fontWeight: 100, + }, + }, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 1, + idealFontSizeJump: 1.1, + outerSizeRatio: 1, + emptySizeRatio: 0, + circlePadding: 4, + backgroundColor: 'rgba(229,229,229,1)', + }} + /> + +); diff --git a/stories/sunburst/sunburst.stories.tsx b/stories/sunburst/sunburst.stories.tsx new file mode 100644 index 0000000000..0593b8db33 --- /dev/null +++ b/stories/sunburst/sunburst.stories.tsx @@ -0,0 +1,36 @@ +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export default { + title: 'Sunburst', + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; + +export { example as mostBasic } from './1_simple'; +export { example as valueFormatted } from './2_value_formatted'; +export { example as valueFormattedWithCategoricalColorPalette } from './3_value_formatted_2'; +export { example as withFillLabels } from './4_fill_labels'; +export { example as donutChartWithFillLabels } from './5_donut'; +export { example as withDirectTextLabels } from './6_pie_chart_labels'; +export { example as someZeroValueSlice } from './7_zero_slice'; +export { example as sunburstWithTwoLayers } from './8_sunburst_two_layers'; +export { example as sunburstWithThreeLayers } from './9_sunburst_three_layers'; + +export { example as withTwoSlices } from './10_2_slice'; +export { example as largeAndSmallSlices } from './11_small_large'; +export { example as veryLargeAndSmall } from './12_very_small'; +export { example as nearFullNearEmpty } from './13_empty'; +export { example as fullAndZeroSlices } from './14_full_zero'; +export { example as singleSlice } from './15_single'; +export { example as singleSmallSice } from './16_single_small'; +export { example as singleVerySmall } from './17_single_very_small'; +export { example as noSlice } from './18_no_sliced'; +export { example as negative } from './19_negative'; + +export { example as totalZero } from './20_total_zero'; +export { example as highNumberOfSlices } from './21_high_pie'; +export { example as counterClockwiseSpecial } from './22_counter_clockwise'; +export { example as clockwiseNoSpecial } from './23_clockwise'; +export { example as linkedLabelsOnly } from './24_linked_label'; +export { example as noLabels } from './25_no_labels'; diff --git a/stories/treemap.tsx b/stories/treemap.tsx deleted file mode 100644 index 06908df95b..0000000000 --- a/stories/treemap.tsx +++ /dev/null @@ -1,345 +0,0 @@ -import { Chart, Datum, Partition, PartitionLayout } from '../src'; -import { mocks } from '../src/mocks/hierarchical/index'; -import { config } from '../src/chart_types/partition_chart/layout/config/config'; -import { arrayToLookup, hueInterpolator } from '../src/chart_types/partition_chart/layout/utils/calcs'; -import { countryDimension, productDimension, regionDimension } from '../src/mocks/hierarchical/dimension_codes'; -import { palettes } from '../src/mocks/hierarchical/palettes'; -import React from 'react'; -import { ShapeTreeNode } from '../src/chart_types/partition_chart/layout/types/viewmodel_types'; -import { categoricalFillColor, colorBrewerCategoricalPastel12 } from './utils/utils'; - -const productLookup = arrayToLookup((d: Datum) => d.sitc1, productDimension); -const regionLookup = arrayToLookup((d: Datum) => d.region, regionDimension); -const countryLookup = arrayToLookup((d: Datum) => d.country, countryDimension); - -// style calcs -const interpolatorCET2s = hueInterpolator(palettes.CET2s.map(([r, g, b]) => [r, g, b, 0.7])); -const interpolatorTurbo = hueInterpolator(palettes.turbo.map(([r, g, b]) => [r, g, b, 0.7])); - -const defaultFillColor = (colorMaker: any) => (d: any, i: number, a: any[]) => colorMaker(i / (a.length + 1)); - -export default { - title: 'Treemap', - parameters: { - info: { - source: false, - }, - }, -}; - -export const OneLayer = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { - textInvertible: true, - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - }, - shape: { - fillColor: defaultFillColor(interpolatorCET2s), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.treemap, - }} - /> - -); -OneLayer.story = { - name: 'One-layer, resizing treemap', -}; - -export const OneLayer2 = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: Datum) => productLookup[d].name, - fillLabel: { - textInvertible: true, - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - valueFont: { - fontWeight: 100, - }, - }, - shape: { - fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex), - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.treemap, - }} - /> - -); -OneLayer2.story = { - name: 'One-layer, ColorBrewer treemap', -}; - -export const MidTwoLayers = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: (d: any) => regionLookup[d].regionName, - fillLabel: { - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - fontFamily: 'Phosphate-Inline', - textColor: 'yellow', - textInvertible: false, - }, - shape: { fillColor: 'rgba(0,0,0,0)' }, - }, - { - groupByRollup: (d: Datum) => d.dest, - nodeLabel: (d: any) => countryLookup[d].name, - fillLabel: { - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - textColor: 'black', - textInvertible: false, - fontWeight: 200, - fontStyle: 'normal', - fontFamily: 'Helvetica', - fontVariant: 'small-caps', - valueFont: { fontWeight: 400, fontStyle: 'italic' }, - }, - shape: { - fillColor: (d: ShapeTreeNode) => { - // primarily, pick color based on parent's index, but then perturb by the index within the parent - return interpolatorTurbo( - (d.parent.sortIndex + d.sortIndex / d.parent.children.length) / (d.parent.parent.children.length + 1), - ); - }, - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.treemap, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 4, - maxFontSize: 84, - idealFontSizeJump: 1.15, - outerSizeRatio: 1, - }} - /> - -); -MidTwoLayers.story = { - name: 'Midsize two-layer treemap', -}; - -export const TwoLayersStressTest = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => d.sitc1, - nodeLabel: (d: any) => productLookup[d].name.toUpperCase(), - fillLabel: { - valueFormatter: () => '', - fontFamily: 'Phosphate-Inline', - textColor: 'rgba(255,255,0, 0.6)', - textInvertible: true, - }, - shape: { - fillColor: (d: ShapeTreeNode) => { - // primarily, pick color based on parent's index, but then perturb by the index within the parent - return interpolatorTurbo(d.sortIndex / (d.parent.children.length + 1)); - }, - }, - }, - { - groupByRollup: (d: Datum) => d.dest, - nodeLabel: (d: any) => countryLookup[d].name, - fillLabel: { - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - textColor: 'black', - textInvertible: true, - fontWeight: 900, - fontStyle: 'normal', - fontFamily: 'Helvetica', - fontVariant: 'normal', - valueFont: { - fontWeight: 100, - }, - }, - shape: { - fillColor: (d: ShapeTreeNode) => { - // primarily, pick color based on parent's index, but then perturb by the index within the parent - return interpolatorTurbo( - (d.parent.sortIndex + d.sortIndex / d.parent.children.length) / (d.parent.parent.children.length + 1), - ); - }, - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.treemap, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 4, - maxFontSize: 84, - idealFontSizeJump: 1.35, - outerSizeRatio: 1, - }} - /> - -); -TwoLayersStressTest.story = { - name: 'Two-layer treemap stress test', -}; - -export const MultiColor = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: () => '', - fillLabel: { - valueFormatter: () => '', - }, - shape: { - fillColor: defaultFillColor(interpolatorCET2s), - }, - }, - { - groupByRollup: (d: Datum) => d.dest, - nodeLabel: (d: any) => countryLookup[d].name, - fillLabel: { - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - textColor: 'rgb(60,60,60,1)', - textInvertible: false, - fontWeight: 100, - fontStyle: 'normal', - fontFamily: 'Din Condensed', - fontVariant: 'normal', - }, - shape: { - fillColor: 'rgba(0,0,0,0)', - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.treemap, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 4, - maxFontSize: 84, - idealFontSizeJump: 1.05, - outerSizeRatio: 1, - }} - /> - -); -MultiColor.story = { - name: 'Each color identifies a region in a (future) legend', -}; - -export const CustomStyle = () => ( - - d.exportVal as number} - valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} - layers={[ - { - groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), - nodeLabel: () => '', - fillLabel: { - valueFormatter: () => '', - }, - shape: { - fillColor: (d: any, i: any, a: any) => { - const shade = Math.pow(0.3 + 0.5 * (i / (a.length - 1)), 1 / 3); - return `rgb(${Math.round(255 * shade)},${Math.round(255 * shade)},${Math.round(255 * shade)})`; - }, - }, - }, - { - groupByRollup: (d: Datum) => d.dest, - nodeLabel: (d: any) => countryLookup[d].name, - fillLabel: { - valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, - textColor: 'rgb(60,60,60,1)', - textInvertible: false, - fontWeight: 600, - fontStyle: 'normal', - fontFamily: 'Courier New', - fontVariant: 'normal', - }, - shape: { - fillColor: 'rgba(0,0,0,0)', - }, - }, - ]} - config={{ - partitionLayout: PartitionLayout.treemap, - margin: { top: 0, bottom: 0, left: 0, right: 0 }, - minFontSize: 8, - maxFontSize: 14, - idealFontSizeJump: 1.05, - outerSizeRatio: 1, - }} - /> - -); -CustomStyle.story = { - name: 'Custom style', -}; diff --git a/stories/treemap/1_one_layer.tsx b/stories/treemap/1_one_layer.tsx new file mode 100644 index 0000000000..c12eba3fbc --- /dev/null +++ b/stories/treemap/1_one_layer.tsx @@ -0,0 +1,41 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import { arrayToLookup, hueInterpolator } from '../../src/chart_types/partition_chart/layout/utils/calcs'; +import { productDimension } from '../../src/mocks/hierarchical/dimension_codes'; +import { palettes } from '../../src/mocks/hierarchical/palettes'; +import React from 'react'; + +const productLookup = arrayToLookup((d: Datum) => d.sitc1, productDimension); + +// style calcs +const interpolatorCET2s = hueInterpolator(palettes.CET2s.map(([r, g, b]) => [r, g, b, 0.7])); + +const defaultFillColor = (colorMaker: any) => (d: any, i: number, a: any[]) => colorMaker(i / (a.length + 1)); + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { + textInvertible: true, + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + }, + shape: { + fillColor: defaultFillColor(interpolatorCET2s), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.treemap, + }} + /> + +); diff --git a/stories/treemap/2_one_layer_2.tsx b/stories/treemap/2_one_layer_2.tsx new file mode 100644 index 0000000000..76a3476445 --- /dev/null +++ b/stories/treemap/2_one_layer_2.tsx @@ -0,0 +1,40 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import { arrayToLookup } from '../../src/chart_types/partition_chart/layout/utils/calcs'; +import { productDimension } from '../../src/mocks/hierarchical/dimension_codes'; +import React from 'react'; +import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types'; +import { categoricalFillColor, colorBrewerCategoricalPastel12 } from '../utils/utils'; + +const productLookup = arrayToLookup((d: Datum) => d.sitc1, productDimension); + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: Datum) => productLookup[d].name, + fillLabel: { + textInvertible: true, + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + valueFont: { + fontWeight: 100, + }, + }, + shape: { + fillColor: (d: ShapeTreeNode) => categoricalFillColor(colorBrewerCategoricalPastel12)(d.sortIndex), + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.treemap, + }} + /> + +); diff --git a/stories/treemap/3_mid_two.tsx b/stories/treemap/3_mid_two.tsx new file mode 100644 index 0000000000..93fc2bcbb1 --- /dev/null +++ b/stories/treemap/3_mid_two.tsx @@ -0,0 +1,74 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import { arrayToLookup, hueInterpolator } from '../../src/chart_types/partition_chart/layout/utils/calcs'; +import { countryDimension, regionDimension } from '../../src/mocks/hierarchical/dimension_codes'; +import { palettes } from '../../src/mocks/hierarchical/palettes'; +import React from 'react'; +import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types'; + +const regionLookup = arrayToLookup((d: Datum) => d.region, regionDimension); +const countryLookup = arrayToLookup((d: Datum) => d.country, countryDimension); + +const interpolatorTurbo = hueInterpolator(palettes.turbo.map(([r, g, b]) => [r, g, b, 0.7])); + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), + nodeLabel: (d: any) => regionLookup[d].regionName, + fillLabel: { + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + fontFamily: 'Phosphate-Inline', + textColor: 'yellow', + textInvertible: false, + }, + shape: { fillColor: 'rgba(0,0,0,0)' }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + fillLabel: { + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + textColor: 'black', + textInvertible: false, + fontWeight: 200, + fontStyle: 'normal', + fontFamily: 'Helvetica', + fontVariant: 'small-caps', + valueFont: { fontWeight: 400, fontStyle: 'italic' }, + }, + shape: { + fillColor: (d: ShapeTreeNode) => { + // primarily, pick color based on parent's index, but then perturb by the index within the parent + return interpolatorTurbo( + (d.parent.sortIndex + d.sortIndex / d.parent.children.length) / (d.parent.parent.children.length + 1), + ); + }, + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.treemap, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 4, + maxFontSize: 84, + idealFontSizeJump: 1.15, + outerSizeRatio: 1, + }} + /> + +); diff --git a/stories/treemap/4_two_layer_stress.tsx b/stories/treemap/4_two_layer_stress.tsx new file mode 100644 index 0000000000..1a0d7bc87e --- /dev/null +++ b/stories/treemap/4_two_layer_stress.tsx @@ -0,0 +1,84 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import { arrayToLookup, hueInterpolator } from '../../src/chart_types/partition_chart/layout/utils/calcs'; +import { countryDimension, productDimension } from '../../src/mocks/hierarchical/dimension_codes'; + +import { palettes } from '../../src/mocks/hierarchical/palettes'; +import React from 'react'; +import { ShapeTreeNode } from '../../src/chart_types/partition_chart/layout/types/viewmodel_types'; + +const productLookup = arrayToLookup((d: Datum) => d.sitc1, productDimension); +const countryLookup = arrayToLookup((d: Datum) => d.country, countryDimension); + +const interpolatorTurbo = hueInterpolator(palettes.turbo.map(([r, g, b]) => [r, g, b, 0.7])); + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => d.sitc1, + nodeLabel: (d: any) => productLookup[d].name.toUpperCase(), + fillLabel: { + valueFormatter: () => '', + fontFamily: 'Phosphate-Inline', + textColor: 'rgba(255,255,0, 0.6)', + textInvertible: true, + }, + shape: { + fillColor: (d: ShapeTreeNode) => { + // primarily, pick color based on parent's index, but then perturb by the index within the parent + return interpolatorTurbo(d.sortIndex / (d.parent.children.length + 1)); + }, + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + fillLabel: { + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + textColor: 'black', + textInvertible: true, + fontWeight: 900, + fontStyle: 'normal', + fontFamily: 'Helvetica', + fontVariant: 'normal', + valueFont: { + fontWeight: 100, + }, + }, + shape: { + fillColor: (d: ShapeTreeNode) => { + // primarily, pick color based on parent's index, but then perturb by the index within the parent + return interpolatorTurbo( + (d.parent.sortIndex + d.sortIndex / d.parent.children.length) / (d.parent.parent.children.length + 1), + ); + }, + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.treemap, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 4, + maxFontSize: 84, + idealFontSizeJump: 1.35, + outerSizeRatio: 1, + }} + /> + +); diff --git a/stories/treemap/5_multicolor.tsx b/stories/treemap/5_multicolor.tsx new file mode 100644 index 0000000000..eb0cc2e808 --- /dev/null +++ b/stories/treemap/5_multicolor.tsx @@ -0,0 +1,68 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import { arrayToLookup, hueInterpolator } from '../../src/chart_types/partition_chart/layout/utils/calcs'; +import { countryDimension } from '../../src/mocks/hierarchical/dimension_codes'; +import { palettes } from '../../src/mocks/hierarchical/palettes'; +import React from 'react'; + +const countryLookup = arrayToLookup((d: Datum) => d.country, countryDimension); + +// style calcs +const interpolatorCET2s = hueInterpolator(palettes.CET2s.map(([r, g, b]) => [r, g, b, 0.7])); + +const defaultFillColor = (colorMaker: any) => (d: any, i: number, a: any[]) => colorMaker(i / (a.length + 1)); + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), + nodeLabel: () => '', + fillLabel: { + valueFormatter: () => '', + }, + shape: { + fillColor: defaultFillColor(interpolatorCET2s), + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + fillLabel: { + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + textColor: 'rgb(60,60,60,1)', + textInvertible: false, + fontWeight: 100, + fontStyle: 'normal', + fontFamily: 'Din Condensed', + fontVariant: 'normal', + }, + shape: { + fillColor: 'rgba(0,0,0,0)', + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.treemap, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 4, + maxFontSize: 84, + idealFontSizeJump: 1.05, + outerSizeRatio: 1, + }} + /> + +); diff --git a/stories/treemap/6_custom_style.tsx b/stories/treemap/6_custom_style.tsx new file mode 100644 index 0000000000..964d55b089 --- /dev/null +++ b/stories/treemap/6_custom_style.tsx @@ -0,0 +1,65 @@ +import { Chart, Datum, Partition, PartitionLayout } from '../../src'; +import { mocks } from '../../src/mocks/hierarchical/index'; +import { config } from '../../src/chart_types/partition_chart/layout/config/config'; +import { arrayToLookup } from '../../src/chart_types/partition_chart/layout/utils/calcs'; +import { countryDimension } from '../../src/mocks/hierarchical/dimension_codes'; +import React from 'react'; + +const countryLookup = arrayToLookup((d: Datum) => d.country, countryDimension); + +export const example = () => ( + + d.exportVal as number} + valueFormatter={(d: number) => `$${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`} + layers={[ + { + groupByRollup: (d: Datum) => countryLookup[d.dest].continentCountry.substr(0, 2), + nodeLabel: () => '', + fillLabel: { + valueFormatter: () => '', + }, + shape: { + fillColor: (d: any, i: any, a: any) => { + const shade = Math.pow(0.3 + 0.5 * (i / (a.length - 1)), 1 / 3); + return `rgb(${Math.round(255 * shade)},${Math.round(255 * shade)},${Math.round(255 * shade)})`; + }, + }, + }, + { + groupByRollup: (d: Datum) => d.dest, + nodeLabel: (d: any) => countryLookup[d].name, + fillLabel: { + valueFormatter: (d: number) => `${config.fillLabel.valueFormatter(Math.round(d / 1000000000))}\xa0Bn`, + textColor: 'rgb(60,60,60,1)', + textInvertible: false, + fontWeight: 600, + fontStyle: 'normal', + fontFamily: 'Courier New', + fontVariant: 'normal', + }, + shape: { + fillColor: 'rgba(0,0,0,0)', + }, + }, + ]} + config={{ + partitionLayout: PartitionLayout.treemap, + margin: { top: 0, bottom: 0, left: 0, right: 0 }, + minFontSize: 8, + maxFontSize: 14, + idealFontSizeJump: 1.05, + outerSizeRatio: 1, + }} + /> + +); diff --git a/stories/treemap/treemap.stories.tsx b/stories/treemap/treemap.stories.tsx new file mode 100644 index 0000000000..840d2d19bb --- /dev/null +++ b/stories/treemap/treemap.stories.tsx @@ -0,0 +1,15 @@ +import { SB_SOURCE_PANEL } from '../utils/storybook'; + +export default { + title: 'Treemap', + parameters: { + options: { selectedPanel: SB_SOURCE_PANEL }, + }, +}; + +export { example as oneLayer } from './1_one_layer'; +export { example as oneLayer2 } from './2_one_layer_2'; +export { example as midTwoLayers } from './3_mid_two'; +export { example as twoLayersStressTest } from './4_two_layer_stress'; +export { example as multiColor } from './5_multicolor'; +export { example as customStyle } from './6_custom_style'; diff --git a/stories/common.ts b/stories/utils/knobs.ts similarity index 95% rename from stories/common.ts rename to stories/utils/knobs.ts index 004bf1291b..4f58b0614d 100644 --- a/stories/common.ts +++ b/stories/utils/knobs.ts @@ -1,6 +1,6 @@ import { select, array } from '@storybook/addon-knobs'; -import { Rotation } from '../src'; +import { Rotation } from '../../src'; export const numberSelect = ( name: string, diff --git a/stories/utils/storybook.ts b/stories/utils/storybook.ts new file mode 100644 index 0000000000..3bf3f2b883 --- /dev/null +++ b/stories/utils/storybook.ts @@ -0,0 +1,3 @@ +export const SB_ACTION_PANEL = 'storybook/actions/panel'; +export const SB_SOURCE_PANEL = 'storybook/source-loader/panel'; +export const SB_KNOBS_PANEL = 'storybookjs/knobs/panel'; diff --git a/wiki/consuming.md b/wiki/consuming.md index 83813ccc03..bfbb28b6ab 100644 --- a/wiki/consuming.md +++ b/wiki/consuming.md @@ -5,7 +5,7 @@ You can import Chart components from the top-level Elastic Chart module. ```js -import { Axis, BarSeries, Chart, getAxisId, getSpecId, Position, ScaleType } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, ScaleType } from '@elastic/charts'; ``` ## Using Elastic Charts in Kibana diff --git a/wiki/overview.md b/wiki/overview.md index 1105c2d0c0..f8bfc5b4d9 100644 --- a/wiki/overview.md +++ b/wiki/overview.md @@ -47,11 +47,11 @@ A spec can be something like the following: ```jsx - - - + + +