diff --git a/cfgov/cfgov/settings/base.py b/cfgov/cfgov/settings/base.py index c41ceeed41..1a478872d7 100644 --- a/cfgov/cfgov/settings/base.py +++ b/cfgov/cfgov/settings/base.py @@ -104,6 +104,7 @@ "filing_instruction_guide", "health_check", "health_check.db", + "wagtailcharts", # Satellites "complaint_search", "countylimits", diff --git a/cfgov/unprocessed/css/on-demand/wagtail-chart.scss b/cfgov/unprocessed/css/on-demand/wagtail-chart.scss new file mode 100644 index 0000000000..e6462b0476 --- /dev/null +++ b/cfgov/unprocessed/css/on-demand/wagtail-chart.scss @@ -0,0 +1,18 @@ +@use 'sass:math'; +@use '@cfpb/cfpb-design-system/src/abstracts' as *; +@import '../main'; + +.o-wagtail-chart { + max-width: math.div(670px, $base-font-size-px) + em; + margin-bottom: math.div(60px, $base-font-size-px) + em; + + &__subtitle { + margin: 0 0 (math.div(30px, $base-font-size-px) + em); + } + + &__footnote { + max-width: math.div(670px, $size-vi) + rem; + padding-top: math.div(15px, $size-vi) + em; + font-size: 0.75em; + } +} diff --git a/cfgov/unprocessed/js/routes/on-demand/wagtail-charts-chart-block.js b/cfgov/unprocessed/js/routes/on-demand/wagtail-charts-chart-block.js new file mode 100644 index 0000000000..493578baab --- /dev/null +++ b/cfgov/unprocessed/js/routes/on-demand/wagtail-charts-chart-block.js @@ -0,0 +1,126 @@ +/* eslint-disable no-undef */ +import pattern from 'patternomaly'; + +/** + * Set default text color to a dark gray + * + * https://www.chartjs.org/docs/latest/general/colors.html + */ +Chart.defaults.color = '#5a5d61'; + +/** + * Takes an array of Chart.js datasets and returns a new array + * with a different line pattern assigned to each dataset's + * borderDash property. + * + * The first line pattern is solid, the second is dashed, + * the third is dotted and all subsequent patterns are dashed + * with an increasingly thicker line. + * + * @param {array} datasets - Array of Chart.js datasets + * @returns {array} Array of Chart.js datasets with borderDash property set + * + * https://www.chartjs.org/docs/latest/samples/line/styling.html + * https://www.chartjs.org/docs/latest/configuration/#dataset-configuration + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash + */ +const patternizeChartLines = (datasets) => { + const DASH_THICKNESS = 5; + const DASH_PATTERNS = [ + [0, 0], // solid + [DASH_THICKNESS, 2], // dashed + [2, 1], // dotted + ]; + return datasets.map((dataset, i) => { + dataset.borderDash = DASH_PATTERNS[i] || [DASH_THICKNESS * i, 2]; + return dataset; + }); +}; + +/** + * Takes an array of Chart.js datasets and returns a new array + * with a different pattern assigned to each dataset's + * backgroundColor property. + * + * Patterns are from the patternomaly library. + * + * @param {array} datasets - Array of Chart.js datasets + * @returns {array} Array of Chart.js datasets with backgroundColor property set + * + * https://www.chartjs.org/docs/latest/general/colors.html#patterns-and-gradients + * https://github.com/ashiguruma/patternomaly + */ +const patternizeChartBars = (datasets) => { + const patterns = [ + 'dot', + 'diagonal', + 'dash', + 'cross-dash', + 'zigzag-vertical', + 'dot-dash', + 'plus', + 'cross', + 'disc', + 'ring', + 'line', + 'line-vertical', + 'weave', + 'zigzag', + 'diagonal-right-left', + 'square', + 'box', + 'triangle', + 'triangle-inverted', + 'diamond', + 'diamond-box', + ]; + return datasets.map((dataset, i) => { + dataset.backgroundColor = dataset.data.map(() => { + // First pattern is just the solid color + if (i === 0) + return Array.isArray(dataset.backgroundColor) + ? dataset.backgroundColor[0] + : dataset.backgroundColor; + return pattern.draw( + patterns[i - 1], + dataset.backgroundColor, + 'rgba(255, 255, 255, 0.6)', + 10, + ); + }); + return dataset; + }); +}; + +/** + * Change the default Chart.js tooltip options + */ +const tooltipOptions = { + yAlign: 'bottom', + displayColors: false, +}; + +/** + * Define a Chart.js plugin for our CFPB customizations + * + * https://www.chartjs.org/docs/latest/developers/plugins.html + */ +const ChartjsPluginCFPB = { + id: 'cfpb-charts', + beforeInit: (chart) => { + chart.config.options.plugins.tooltip = tooltipOptions; + + if (chart.config.type === 'line') { + patternizeChartLines(chart.config.data.datasets); + } + + if (chart.config.type === 'bar') { + patternizeChartBars(chart.config.data.datasets); + } + + chart.update(); + }, +}; + +Chart.register(ChartjsPluginStacked100.default); +Chart.register({ ChartjsPluginCFPB }); diff --git a/cfgov/v1/atomic_elements/charts.py b/cfgov/v1/atomic_elements/charts.py new file mode 100644 index 0000000000..9755084434 --- /dev/null +++ b/cfgov/v1/atomic_elements/charts.py @@ -0,0 +1,109 @@ +from wagtail import blocks + +from wagtailcharts.blocks import ChartBlock as WagtailChartBlock + +from v1 import blocks as v1_blocks + + +CHART_TYPES = ( + ("line", "Line Chart"), + ("bar", "Vertical Bar Chart"), + ("bar_horizontal", "Horizontal Bar Chart"), +) + + +CHART_COLORS = ( + ("#20aa3f", "CFPB Green"), + ("#254b87", "Navy"), + ("#7eb7e8", "Pacific 60"), + ("#ffb858", "Gold 80"), + ("#c55998", "Purple 80"), + ("#addc91", "Green 60"), + ("#1fa040", "Mid Dark Green"), + ("#257675", "Teal"), + ("#89b6b5", "Teal 60"), + ("#d14124", "Red"), + ("#e79e8e", "Red 60"), + ("#0072ce", "Pacific"), + ("#254b87", "Navy"), + ("#dc731c", "Dark Gold"), + ("#745745", "Dark Neutral"), + ("#baa496", "Neutral 60"), + ("#dc9cbf", "Purple 50"), + ("#a01b68", "Dark Purple"), + ("#d2d3d5", "Gray 20"), +) + + +class ChartBlock(WagtailChartBlock): + eyebrow = blocks.CharBlock( + required=False, + help_text=( + "Optional: Adds an H5 eyebrow above H1 heading text. " + "Only use in conjunction with heading." + ), + label="Pre-heading", + ) + title = v1_blocks.HeadingBlock(required=False) + intro = blocks.RichTextBlock(required=False, icon="edit") + description = blocks.TextBlock( + required=False, help_text="Accessible description of the chart content" + ) + data_source = blocks.TextBlock( + required=False, + help_text="Description of the data source", + ) + date_published = blocks.CharBlock( + required=False, help_text="When the underlying data was published" + ) + download_text = blocks.CharBlock( + required=False, + help_text="Custom text for the chart download field. Required to " + "display a download link.", + ) + download_file = blocks.CharBlock( + required=False, + help_text="Location of a file to download", + ) + notes = blocks.TextBlock(required=False, help_text="Note about the chart") + + def __init__(self, **kwargs): + # Always override chart_types and colors with ours + super().__init__( + chart_types=CHART_TYPES, colors=CHART_COLORS, **kwargs + ) + + # Create a more user-friendly ordering of this block's child blocks. + # + # This puts our content-focused blocks in front of the + # chart-configuration blocks we inherit from wagtailcharts. + # + # child_blocks is an OrderedDict that comes from Wagtail's + # StructBlock. This just calls OrderedDict.move_to_end() in the + # order we want the blocks to appear. + self.child_blocks.move_to_end("chart_type") + self.child_blocks.move_to_end("datasets") + self.child_blocks.move_to_end("settings") + + # We also want the eyebrow to appear above the title field. + self.child_blocks.move_to_end("eyebrow", last=False) + + class Meta: + label = "Chart" + icon = "image" + template = "v1/includes/organisms/wagtail-chart.html" + + # Load wagtailcharts scripts when block is included on a page instead of + # by rendering a {% render_charts %} template tag. + # https://github.com/overcastsoftware/wagtailcharts/blob/v0.5/wagtailcharts/templates/wagtailcharts/tags/render_charts.html + class Media: + js = [ + "wagtailcharts/js/accounting.js?staticroot", + "wagtailcharts/js/chart-types.js?staticroot", + "wagtailcharts/js/chart.js?staticroot", + "wagtailcharts/js/stacked-100.js?staticroot", + "wagtailcharts/js/chartjs-plugin-datalabels.min.js?staticroot", + "wagtail-charts-chart-block.js", + "wagtailcharts/js/wagtailcharts.js?staticroot", + ] + css = ["wagtail-chart.css"] diff --git a/cfgov/v1/jinja2/v1/includes/organisms/wagtail-chart.html b/cfgov/v1/jinja2/v1/includes/organisms/wagtail-chart.html new file mode 100644 index 0000000000..29c850db96 --- /dev/null +++ b/cfgov/v1/jinja2/v1/includes/organisms/wagtail-chart.html @@ -0,0 +1,60 @@ +{# ========================================================================== + + wagtail-chart + + ========================================================================== + + Description: + + Create a chart block based on wagtailcharts + + ========================================================================== #} + +{# TODO What should this o-class be? #} +
{{ value.subtitle }}
+ {% endif %} + + {# Copied from wagtailcharts/templates/wagtailcharts/blocks/chart_block.html #} + + +
+ {% if value.data_source %}
+ Source: {{value.data_source}}
+ {% endif %}
+ {% if value.date_published %}
+ Date published: {{value.date_published}}
+ {% endif %}
+
+ {% if value.download_text and value.download_file %}
+ Download:
+ {{value.download_text}}
+ {% endif %}
+
+ {% if value.notes %}
+ Notes: {{value.notes}}
+ {% endif %}
+
<h3>
at the top of this block. Also adds a wrapping <div>
whose id
attribute comes from a slugified version of this heading, creating an anchor that can be used when linking to this part of the page.', 'required': False}), 93: ('wagtail.blocks.BooleanBlock', (), {'required': False}), 94: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add a horizontal rule line to top of expandable group.', 'required': False}), 95: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add FAQ schema markup to expandables.', 'label': 'Uses FAQ schema', 'required': False}), 96: ('wagtail.blocks.StreamBlock', [[('paragraph', 29), ('well', 41), ('links', 33), ('info_unit_group', 53)]], {'blank': True}), 97: ('wagtail.blocks.StructBlock', [[('label', 6), ('icon', 6), ('is_bordered', 93), ('is_midtone', 93), ('is_expanded', 93), ('is_expanded_padding', 93), ('content', 96)]], {}), 98: ('wagtail.blocks.ListBlock', (97,), {}), 99: ('wagtail.blocks.StructBlock', [[('heading', 92), ('body', 29), ('is_accordion', 93), ('has_top_rule_line', 94), ('is_faq', 95), ('expandables', 98)]], {}), 100: ('wagtail.blocks.RegexBlock', (), {'error_messages': {'invalid': 'The YouTube video ID is in the wrong format.'}, 'help_text': 'Enter the YouTube video ID, which is located at the end of the video URL, after "v=". For example, the video ID for https://www.youtube.com/watch?v=1V0Ax9OIc84 is 1V0Ax9OIc84.', 'label': 'YouTube video ID', 'regex': '^[\\w-]{11}$', 'required': False}), 101: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Optional thumbnail image to show before and after the video plays. If the thumbnail image is not set here, the video player will default to showing the thumbnail that was set in (or automatically chosen by) YouTube.', 'required': False}), 102: ('wagtail.blocks.StructBlock', [[('video_id', 100), ('thumbnail_image', 101)]], {}), 103: ('wagtail.blocks.RawHTMLBlock', (), {'label': 'Raw HTML block'}), 104: ('wagtail.blocks.CharBlock', (), {'required': True}), 105: ('wagtail.blocks.TextBlock', (), {'help_text': 'Accessible description of the chart content', 'required': True}), 106: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('bar', 'Bar'), ('datetime', 'Date/time'), ('line', 'Line'), ('tilemap', 'Tile grid map')]}), 107: ('wagtail.blocks.TextBlock', (), {'help_text': "URL of the chart's data source or an array of JSON data", 'required': True, 'rows': 2}), 108: ('wagtail.blocks.TextBlock', (), {'help_text': 'For charts pulling from a separate source file, include a list of the column headers (from a CSV file) or keys (from a JSON file) to include in the chart as ["HEADER/KEY1", "HEADER/KEY2"]. To change how the data is labeled in the chart, include the correct labels with the format [{"key": "HEADER/KEY1", "label": "NEWLABEL"}, {"key": "HEADER/KEY2", "label": "NEWLABEL2"}]', 'required': False}), 109: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': 'Uncheck this option to initially only show the first data series in the chart. Leave checked to show all data series by default. Users can always turn data series on or off by interacting with the chart legend. ', 'required': False}), 110: ('wagtail.blocks.TextBlock', (), {'help_text': 'The column header (CSV), key or data array (JSON) to include as the source of x-axis values.', 'required': False}), 111: ('wagtail.blocks.CharBlock', (), {'help_text': "Name the javascript function in chart-hooks.js to run on the provided data before handing it to the chart. Can also provide '___'-separated arguments to this function which are passed in as arguments 2 to n", 'required': False}), 112: ('wagtail.blocks.TextBlock', (), {'help_text': 'If the chart needs the option for users to filter the data shown, for example by date or geographic region, provide the JSON objects to filter on, in the format {key: "KEY", "label": "LABEL"}', 'required': False}), 113: ('wagtail.blocks.TextBlock', (), {'help_text': 'A JSON object with style overrides for the underlying Highcharts chart. No object merging is done, nested objects should be referenced with dot notation: {"tooltip.shape": "circle"}', 'required': False}), 114: ('wagtail.blocks.IntegerBlock', (), {'blank': True, 'help_text': 'A number to determine how many months of the data are projected values', 'max_value': 12, 'min_value': 0, 'null': True, 'required': False}), 115: ('wagtail.blocks.CharBlock', (), {'help_text': 'Attribution for the data source', 'required': False}), 116: ('wagtail.blocks.CharBlock', (), {'help_text': 'Location of a file to download, if different from the data source', 'required': False}), 117: ('wagtail.blocks.TextBlock', (), {'help_text': 'General chart information', 'required': False}), 118: ('wagtail.blocks.StructBlock', [[('title', 104), ('subtitle', 27), ('description', 105), ('figure_number', 6), ('chart_type', 106), ('data_source', 107), ('data_series', 108), ('show_all_series_by_default', 109), ('x_axis_source', 110), ('transform', 111), ('x_axis_label', 6), ('y_axis_label', 6), ('filters', 112), ('style_overrides', 113), ('projected_months', 114), ('source_credits', 115), ('date_published', 58), ('download_text', 59), ('download_file', 116), ('notes', 117)]], {'group': 'Not commonly used'}), 119: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('bar', 'Bar | % y-axis values'), ('line', 'Line | millions/billions y-axis values'), ('line-index', 'Line-Index | integer y-axis values'), ('tile_map', 'Tile Map | grid-like USA map')]}), 120: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('blue', 'Blue'), ('gold', 'Gold'), ('green', 'Green'), ('navy', 'Navy'), ('neutral', 'Neutral'), ('purple', 'Purple'), ('teal', 'Teal')], 'help_text': 'Chart\'s color scheme. See "https://github.com/cfpb/cfpb-chart-builder#createchart-options-".', 'required': False}), 121: ('wagtail.blocks.CharBlock', (), {'help_text': 'Location of the chart\'s data source relative to "https://files.consumerfinance.gov/data/". For example,"consumer-credit-trends/auto-loans/num_data_AUT.csv".', 'required': True}), 122: ('wagtail.blocks.DateBlock', (), {'help_text': 'Automatically generated when CCT cron job runs'}), 123: ('wagtail.blocks.CharBlock', (), {'help_text': 'Briefly summarize the chart for visually impaired users.', 'required': True}), 124: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add a horizontal rule line to top of chart block.', 'required': False}), 125: ('wagtail.blocks.DateBlock', (), {'help_text': 'Month of latest entry in dataset'}), 126: ('wagtail.blocks.CharBlock', (), {'help_text': 'Optional metadata for the chart to use. For example, with CCT this would be the chart\'s "group".', 'required': False}), 127: ('wagtail.blocks.CharBlock', (), {'help_text': 'Text to display as a footnote. For example, "Data from the last six months are not final."', 'required': False}), 128: ('wagtail.blocks.CharBlock', (), {'help_text': 'Custom y-axis label. NOTE: Line-Index chart y-axis is not overridable with this field!', 'required': False}), 129: ('wagtail.blocks.StructBlock', [[('title', 104), ('chart_type', 119), ('color_scheme', 120), ('data_source', 121), ('date_published', 122), ('description', 123), ('has_top_rule_line', 124), ('last_updated_projected_data', 125), ('metadata', 126), ('note', 127), ('y_axis_label', 128)]], {'group': 'Not commonly used'}), 130: ('wagtail.blocks.CharBlock', (), {'form_classname': 'title', 'required': True}), 131: ('wagtail.blocks.CharBlock', (), {'help_text': 'Chart summary for visually impaired users.', 'required': False}), 132: ('wagtail.blocks.CharBlock', (), {'help_text': 'Text for "Note" section of footnotes.', 'required': False}), 133: ('wagtail.blocks.StructBlock', [[('content_block', 1), ('title', 130), ('description', 131), ('note', 132), ('has_top_rule_line', 124)]], {'group': 'Not commonly used'}), 134: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this box to allow the archival section to display. No section will appear if there are no archival downloads.', 'required': False}), 135: ('wagtail.blocks.StructBlock', [[('show_archives', 134)]], {'group': 'Not commonly used'}), 136: ('wagtail.blocks.CharBlock', (), {'help_text': 'Market identifier, e.g. AUT', 'max_length': 20, 'required': True}), 137: ('wagtail.blocks.CharBlock', (), {'help_text': 'Number of originations, e.g. 1.2 million', 'max_length': 20}), 138: ('wagtail.blocks.CharBlock', (), {'help_text': 'Total dollar value of originations, e.g. $3.4 billion', 'max_length': 20}), 139: ('wagtail.blocks.CharBlock', (), {'help_text': 'Percentage change, e.g. 5.6% increase', 'max_length': 20}), 140: ('wagtail.blocks.CharBlock', (), {'help_text': 'Descriptive sentence, e.g. Auto loans originated', 'max_length': 100}), 141: ('wagtail.blocks.CharBlock', (), {'help_text': 'Descriptive sentence, e.g. Dollar volume of new loans', 'max_length': 100}), 142: ('wagtail.blocks.CharBlock', (), {'help_text': 'Descriptive sentence, e.g. In year-over-year originations', 'max_length': 100}), 143: ('wagtail.blocks.DateBlock', (), {'help_text': 'Month of latest entry in dataset for inquiry data', 'max_length': 20, 'required': False}), 144: ('wagtail.blocks.CharBlock', (), {'help_text': 'Percentage change, e.g. 5.6% increase', 'max_length': 20, 'required': False}), 145: ('wagtail.blocks.CharBlock', (), {'help_text': 'Descriptive sentence, e.g. In year-over-year inquiries', 'max_length': 100, 'required': False}), 146: ('wagtail.blocks.DateBlock', (), {'help_text': 'Month of latest entry in dataset for credit tightness data', 'max_length': 20, 'required': False}), 147: ('wagtail.blocks.CharBlock', (), {'help_text': 'Descriptive sentence, e.g. In year-over-year credit tightness', 'max_length': 100, 'required': False}), 148: ('wagtail.images.blocks.ImageChooserBlock', (), {'icon': 'image', 'required': False}), 149: ('wagtail.blocks.StructBlock', [[('market_key', 136), ('num_originations', 137), ('value_originations', 138), ('year_over_year_change', 139), ('last_updated_projected_data', 125), ('num_originations_text', 140), ('value_originations_text', 141), ('year_over_year_change_text', 142), ('inquiry_month', 143), ('inquiry_year_over_year_change', 144), ('inquiry_year_over_year_change_text', 145), ('tightness_month', 146), ('tightness_year_over_year_change', 144), ('tightness_year_over_year_change_text', 147), ('image', 148)]], {'group': 'Not commonly used'}), 150: ('jobmanager.blocks.JobListingTable', (), {'group': 'Not commonly used'}), 151: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add a horizontal rule line to top of FAQ group.', 'required': False}), 152: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to show horizontal rule lines between FAQ items.', 'label': 'Show rule lines between items', 'required': False}), 153: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('h2', 'h2'), ('h3', 'h3'), ('h4', 'h4'), ('p', 'p')], 'help_text': 'HTML tag for questions.'}), 154: ('wagtail.blocks.CharBlock', (), {'blank': True, 'help_text': "Add an optional anchor link tag for this question. Tag should be unique and use dashes or underscores for separation instead of spaces (ie, 'question-one-tag')", 'max_length': 500, 'required': False}), 155: ('wagtail.blocks.CharBlock', (), {'max_length': 500}), 156: ('wagtail.blocks.StreamBlock', [[('full_width_text', 42), ('info_unit_group', 53)]], {}), 157: ('wagtail.blocks.StructBlock', [[('anchor_tag', 154), ('question', 155), ('answer', 156)]], {}), 158: ('wagtail.blocks.ListBlock', (157,), {'label': 'FAQ items'}), 159: ('wagtail.blocks.StructBlock', [[('has_top_rule_line', 151), ('lines_between_items', 152), ('question_tag', 153), ('faq_items', 158)]], {})}),
+ ),
+ migrations.AlterField(
+ model_name='documentdetailpage',
+ name='content',
+ field=wagtail.fields.StreamField([('full_width_text', 42), ('expandable', 56), ('expandable_group', 61), ('notification', 66), ('simple_chart', 83), ('table', 25), ('crc_table', 85), ('case_docket_table', 87), ('wagtailchart_block', 123)], blank=True, block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'icon': 'edit'}), 1: ('wagtail.blocks.RichTextBlock', (), {}), 2: ('wagtail.blocks.CharBlock', (), {'help_text': '\n ID will be auto-generated on save.\n However, you may enter some human-friendly text that\n will be incorporated to make it easier to read.\n ', 'label': 'ID for this content block', 'required': False}), 3: ('wagtail.blocks.StructBlock', [[('link_id', 2)]], {}), 4: ('wagtail.blocks.StructBlock', [[('content_block', 1), ('anchor_link', 3)]], {}), 5: ('wagtail_footnotes.blocks.RichTextBlockWithFootnotes', (), {'features': ['anchor-identifier', 'h2', 'h3', 'h4', 'h5', 'hr', 'ol', 'ul', 'bold', 'italic', 'superscript', 'blockquote', 'link', 'document-link', 'image', 'icon', 'footnotes']}), 6: ('wagtail.blocks.CharBlock', (), {'required': False}), 7: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4'), ('h5', 'H5')]}), 8: ('wagtail.blocks.CharBlock', (), {'help_text': 'Input the name of an icon to appear to the left of the heading. E.g., approved, help-round, etc. See full list of icons', 'required': False}), 9: ('wagtail.blocks.StructBlock', [[('text', 6), ('level', 7), ('icon', 8)]], {'required': False}), 10: ('wagtail.images.blocks.ImageChooserBlock', (), {'required': False}), 11: ('wagtail.blocks.CharBlock', (), {'help_text': "No character limit, but be as succinct as possible. If the image is decorative (i.e., a screenreader wouldn't have anything useful to say about it), leave this field blank.", 'required': False}), 12: ('wagtail.blocks.StructBlock', [[('upload', 10), ('alt', 11)]], {}), 13: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('full', 'Full width'), (470, '470px'), (270, '270px'), (170, '170px')]}), 14: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('right', 'right'), ('left', 'left')], 'help_text': 'Does not apply if the image is full-width'}), 15: ('wagtail.blocks.RichTextBlock', (), {'label': 'Caption', 'required': False}), 16: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': 'Check to add a horizontal rule line to bottom of inset.', 'label': 'Has bottom rule line', 'required': False}), 17: ('wagtail.blocks.StructBlock', [[('image', 12), ('image_width', 13), ('image_position', 14), ('text', 15), ('is_bottom_rule', 16)]], {}), 18: ('wagtail.blocks.MultipleChoiceBlock', [], {'choices': [('is_full_width', 'Display the table at full width'), ('stack_on_mobile', 'Stack the table columns on mobile')], 'required': False}), 19: ('wagtail.blocks.CharBlock', (), {}), 20: ('wagtail.blocks.FloatBlock', (), {}), 21: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'ol', 'ul', 'link', 'document-link', 'superscript']}), 22: ('wagtail_footnotes.blocks.RichTextBlockWithFootnotes', (), {'features': ['bold', 'italic', 'ol', 'ul', 'link', 'document-link', 'superscript', 'footnotes']}), 23: ('wagtail.contrib.typed_table_block.blocks.TypedTableBlock', [[('text', 19), ('numeric', 20), ('rich_text', 21), ('rich_text_with_footnotes', 22)]], {}), 24: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'document-link'], 'required': False}), 25: ('wagtail.blocks.StructBlock', [[('heading', 9), ('text_introduction', 6), ('options', 18), ('data', 23), ('caption', 24)]], {}), 26: ('wagtail.blocks.TextBlock', (), {}), 27: ('wagtail.blocks.TextBlock', (), {'required': False}), 28: ('wagtail.blocks.StructBlock', [[('body', 26), ('citation', 27)]], {}), 29: ('wagtail.blocks.RichTextBlock', (), {'required': False}), 30: ('wagtail.blocks.CharBlock', (), {'help_text': 'Add an ARIA label if the link text does not describe the destination of the link (e.g. has ambiguous text like "Learn more" that is not descriptive on its own).', 'required': False}), 31: ('wagtail.blocks.CharBlock', (), {'default': '/', 'required': False}), 32: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'required': False}), 33: ('wagtail.blocks.StructBlock', [[('text', 6), ('aria_label', 30), ('url', 31), ('is_link_boldface', 32)]], {}), 34: ('wagtail.blocks.StructBlock', [[('slug_text', 6), ('paragraph_text', 29), ('button', 33)]], {}), 35: ('wagtail.blocks.ListBlock', (33,), {}), 36: ('wagtail.blocks.StructBlock', [[('heading', 6), ('paragraph', 29), ('links', 35)]], {}), 37: ('v1.blocks.ReusableTextChooserBlock', ('v1.ReusableText',), {}), 38: ('v1.blocks.ReusableNotificationChooserBlock', ('v1.ReusableNotification',), {}), 39: ('v1.blocks.EmailSignUpChooserBlock', (), {}), 40: ('wagtail.blocks.RichTextBlock', (), {'label': 'Well', 'required': False}), 41: ('wagtail.blocks.StructBlock', [[('content', 40)]], {}), 42: ('wagtail.blocks.StreamBlock', [[('content', 0), ('content_with_anchor', 4), ('content_with_footnotes', 5), ('heading', 9), ('image', 17), ('table', 25), ('quote', 28), ('cta', 34), ('related_links', 36), ('reusable_text', 37), ('reusable_notification', 38), ('email_signup', 39), ('well', 41)]], {}), 43: ('wagtail.blocks.BooleanBlock', (), {'required': False}), 44: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('50-50', '50/50'), ('33-33-33', '33/33/33'), ('25-75', '25/75')], 'help_text': 'Choose the number and width of info unit columns.', 'label': 'Format'}), 45: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': "Check this to link all images and headings to the URL of the first link in their unit's list, if there is a link.", 'required': False}), 46: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add a horizontal rule line to top of info unit group.', 'required': False}), 47: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to show horizontal rule lines between info units.', 'label': 'Show rule lines between items', 'required': False}), 48: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('none', 'None'), ('rounded', 'Rounded corners'), ('circle', 'Circle')], 'help_text': 'Adds a border-radius class to images in this group, allowing for a rounded or circular border.', 'label': 'Border radius for images?', 'required': False}), 49: ('wagtail.blocks.StructBlock', [[('text', 6), ('level', 7), ('icon', 8)]], {'default': {'level': 'h3'}, 'required': False}), 50: ('wagtail.blocks.RichTextBlock', (), {'blank': True, 'required': False}), 51: ('wagtail.blocks.ListBlock', (33,), {'required': False}), 52: ('wagtail.blocks.StructBlock', [[('image', 12), ('heading', 49), ('body', 50), ('links', 51)]], {}), 53: ('wagtail.blocks.ListBlock', (52,), {'default': []}), 54: ('wagtail.blocks.StructBlock', [[('format', 44), ('heading', 9), ('intro', 29), ('link_image_and_heading', 45), ('has_top_rule_line', 46), ('lines_between_items', 47), ('border_radius_image', 48), ('info_units', 53)]], {}), 55: ('wagtail.blocks.StreamBlock', [[('paragraph', 29), ('well', 41), ('links', 33), ('info_unit_group', 54)]], {'blank': True}), 56: ('wagtail.blocks.StructBlock', [[('label', 6), ('icon', 6), ('is_bordered', 43), ('is_midtone', 43), ('is_expanded', 43), ('is_expanded_padding', 43), ('content', 55)]], {}), 57: ('wagtail.blocks.CharBlock', (), {'help_text': 'Added as an <h3>
at the top of this block. Also adds a wrapping <div>
whose id
attribute comes from a slugified version of this heading, creating an anchor that can be used when linking to this part of the page.', 'required': False}), 58: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add a horizontal rule line to top of expandable group.', 'required': False}), 59: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add FAQ schema markup to expandables.', 'label': 'Uses FAQ schema', 'required': False}), 60: ('wagtail.blocks.ListBlock', (56,), {}), 61: ('wagtail.blocks.StructBlock', [[('heading', 57), ('body', 29), ('is_accordion', 43), ('has_top_rule_line', 58), ('is_faq', 59), ('expandables', 60)]], {}), 62: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('information', 'Information'), ('warning', 'Warning')]}), 63: ('wagtail.blocks.CharBlock', (), {'help_text': 'The main notification message to display.', 'required': True}), 64: ('wagtail.blocks.TextBlock', (), {'help_text': 'Explanation text appears below the message in smaller type.', 'required': False}), 65: ('wagtail.blocks.ListBlock', (33,), {'help_text': 'Links appear on their own lines below the explanation.', 'required': False}), 66: ('wagtail.blocks.StructBlock', [[('type', 62), ('message', 63), ('explanation', 64), ('links', 65)]], {}), 67: ('wagtail.blocks.CharBlock', (), {'required': True}), 68: ('wagtail.blocks.TextBlock', (), {'help_text': 'Accessible description of the chart content', 'required': True}), 69: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('bar', 'Bar'), ('datetime', 'Date/time'), ('line', 'Line'), ('tilemap', 'Tile grid map')]}), 70: ('wagtail.blocks.TextBlock', (), {'help_text': "URL of the chart's data source or an array of JSON data", 'required': True, 'rows': 2}), 71: ('wagtail.blocks.TextBlock', (), {'help_text': 'For charts pulling from a separate source file, include a list of the column headers (from a CSV file) or keys (from a JSON file) to include in the chart as ["HEADER/KEY1", "HEADER/KEY2"]. To change how the data is labeled in the chart, include the correct labels with the format [{"key": "HEADER/KEY1", "label": "NEWLABEL"}, {"key": "HEADER/KEY2", "label": "NEWLABEL2"}]', 'required': False}), 72: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': 'Uncheck this option to initially only show the first data series in the chart. Leave checked to show all data series by default. Users can always turn data series on or off by interacting with the chart legend. ', 'required': False}), 73: ('wagtail.blocks.TextBlock', (), {'help_text': 'The column header (CSV), key or data array (JSON) to include as the source of x-axis values.', 'required': False}), 74: ('wagtail.blocks.CharBlock', (), {'help_text': "Name the javascript function in chart-hooks.js to run on the provided data before handing it to the chart. Can also provide '___'-separated arguments to this function which are passed in as arguments 2 to n", 'required': False}), 75: ('wagtail.blocks.TextBlock', (), {'help_text': 'If the chart needs the option for users to filter the data shown, for example by date or geographic region, provide the JSON objects to filter on, in the format {key: "KEY", "label": "LABEL"}', 'required': False}), 76: ('wagtail.blocks.TextBlock', (), {'help_text': 'A JSON object with style overrides for the underlying Highcharts chart. No object merging is done, nested objects should be referenced with dot notation: {"tooltip.shape": "circle"}', 'required': False}), 77: ('wagtail.blocks.IntegerBlock', (), {'blank': True, 'help_text': 'A number to determine how many months of the data are projected values', 'max_value': 12, 'min_value': 0, 'null': True, 'required': False}), 78: ('wagtail.blocks.CharBlock', (), {'help_text': 'Attribution for the data source', 'required': False}), 79: ('wagtail.blocks.CharBlock', (), {'help_text': 'When the underlying data was published', 'required': False}), 80: ('wagtail.blocks.CharBlock', (), {'help_text': 'Custom text for the chart download field. Required to display a download link.', 'required': False}), 81: ('wagtail.blocks.CharBlock', (), {'help_text': 'Location of a file to download, if different from the data source', 'required': False}), 82: ('wagtail.blocks.TextBlock', (), {'help_text': 'General chart information', 'required': False}), 83: ('wagtail.blocks.StructBlock', [[('title', 67), ('subtitle', 27), ('description', 68), ('figure_number', 6), ('chart_type', 69), ('data_source', 70), ('data_series', 71), ('show_all_series_by_default', 72), ('x_axis_source', 73), ('transform', 74), ('x_axis_label', 6), ('y_axis_label', 6), ('filters', 75), ('style_overrides', 76), ('projected_months', 77), ('source_credits', 78), ('date_published', 79), ('download_text', 80), ('download_file', 81), ('notes', 82)]], {}), 84: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link']}), 85: ('wagtail.blocks.StructBlock', [[('website', 84), ('phone', 84), ('mailing_address', 84)]], {}), 86: ('wagtail.blocks.ListBlock', (v1.atomic_elements.tables.CaseDocketEvent,), {'collapsed': True, 'min_num': 1}), 87: ('wagtail.blocks.StructBlock', [[('events', 86)]], {}), 88: ('wagtail.blocks.CharBlock', (), {'help_text': 'Optional: Adds an H5 eyebrow above H1 heading text. Only use in conjunction with heading.', 'label': 'Pre-heading', 'required': False}), 89: ('wagtail.blocks.RichTextBlock', (), {'icon': 'edit', 'required': False}), 90: ('wagtail.blocks.TextBlock', (), {'help_text': 'Accessible description of the chart content', 'required': False}), 91: ('wagtail.blocks.TextBlock', (), {'help_text': 'Description of the data source', 'required': False}), 92: ('wagtail.blocks.CharBlock', (), {'help_text': 'Location of a file to download', 'required': False}), 93: ('wagtail.blocks.TextBlock', (), {'help_text': 'Note about the chart', 'required': False}), 94: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('line', 'Line Chart'), ('bar', 'Vertical Bar Chart'), ('bar_horizontal', 'Horizontal Bar Chart')], 'label': 'Chart Type'}), 95: ('wagtail.blocks.TextBlock', (), {'default': '{"data":[], "options":{}}'}), 96: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Show legend', 'required': False}), 97: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Use HTML legend', 'required': False}), 98: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('top', 'Top'), ('bottom', 'Bottom'), ('left', 'Left'), ('right', 'Right')], 'group': 'General', 'label': 'Legend position'}), 99: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Reverse legend', 'required': False}), 100: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Show values on chart', 'required': False}), 101: ('wagtail.blocks.IntegerBlock', (), {'default': 1, 'group': 'General', 'label': 'Precision in labels/tooltips'}), 102: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'group': 'General', 'label': 'Show Chart Grid', 'required': False}), 103: ('wagtail.blocks.CharBlock', (), {'group': 'General', 'label': 'X axis label', 'required': False}), 104: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('none', 'No stacking'), ('stacked', 'Stacked'), ('stacked_100', 'Stacked 100%')], 'group': 'General', 'label': 'Stacking'}), 105: ('wagtail.blocks.CharBlock', (), {'group': 'General', 'label': 'Unit override', 'required': False}), 106: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis minimum value', 'required': False}), 107: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis maximum value', 'required': False}), 108: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis step size', 'required': False}), 109: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis label', 'required': False}), 110: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('number', 'Numerical'), ('percentage', 'Percentage')], 'group': 'Left_Axis', 'label': 'Left Y axis data type', 'required': False}), 111: ('wagtail.blocks.IntegerBlock', (), {'default': 0, 'group': 'Left_Axis', 'label': 'Left Y axis tick precision'}), 112: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'group': 'Left_Axis', 'label': 'Show left axis numbers', 'required': False}), 113: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis minimum value', 'required': False}), 114: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis maximum value', 'required': False}), 115: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis step size', 'required': False}), 116: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis label', 'required': False}), 117: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('number', 'Numerical'), ('percentage', 'Percentage')], 'group': 'Right_Axis', 'label': 'Right Y axis data type', 'required': False}), 118: ('wagtail.blocks.IntegerBlock', (), {'default': 0, 'group': 'Right_Axis', 'label': 'Right Y axis tick precision'}), 119: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'group': 'Right_Axis', 'label': 'Show right axis numbers', 'required': False}), 120: ('wagtail.blocks.IntegerBlock', (), {'default': 2, 'group': 'Pie_Chart', 'label': 'Width of pie slice border'}), 121: ('wagtail.blocks.CharBlock', (), {'default': '#fff', 'group': 'Pie_Chart', 'label': 'Color of pie slice border'}), 122: ('wagtail.blocks.StructBlock', [[('show_legend', 96), ('html_legend', 97), ('legend_position', 98), ('reverse_legend', 99), ('show_values_on_chart', 100), ('precision', 101), ('show_grid', 102), ('x_label', 103), ('stacking', 104), ('unit_override', 105), ('y_left_min', 106), ('y_left_max', 107), ('y_left_step_size', 108), ('y_left_label', 109), ('y_left_data_type', 110), ('y_left_precision', 111), ('y_left_show', 112), ('y_right_min', 113), ('y_right_max', 114), ('y_right_step_size', 115), ('y_right_label', 116), ('y_right_data_type', 117), ('y_right_precision', 118), ('y_right_show', 119), ('pie_border_width', 120), ('pie_border_color', 121)]], {}), 123: ('wagtail.blocks.StructBlock', [[('eyebrow', 88), ('title', 9), ('intro', 89), ('description', 90), ('data_source', 91), ('date_published', 79), ('download_text', 80), ('download_file', 92), ('notes', 93), ('chart_type', 94), ('datasets', 95), ('settings', 122)]], {'colors': (('#20aa3f', 'CFPB Green'), ('#254b87', 'Navy'), ('#7eb7e8', 'Pacific 60'), ('#ffb858', 'Gold 80'), ('#c55998', 'Purple 80'), ('#addc91', 'Green 60'), ('#1fa040', 'Mid Dark Green'), ('#257675', 'Teal'), ('#89b6b5', 'Teal 60'), ('#d14124', 'Red'), ('#e79e8e', 'Red 60'), ('#0072ce', 'Pacific'), ('#254b87', 'Navy'), ('#dc731c', 'Dark Gold'), ('#745745', 'Dark Neutral'), ('#baa496', 'Neutral 60'), ('#dc9cbf', 'Purple 50'), ('#a01b68', 'Dark Purple'), ('#d2d3d5', 'Gray 20'))})}),
+ ),
+ migrations.AlterField(
+ model_name='legacyblogpage',
+ name='content',
+ field=wagtail.fields.StreamField([('full_width_text', 42), ('info_unit_group', 53), ('expandable', 56), ('well', 41), ('video_player', 59), ('email_signup', 39), ('simple_chart', 76), ('faq_schema', 85), ('how_to_schema', 95), ('wagtailchart_block', 131), ('content', 132), ('reusable_text', 37)], block_lookup={0: ('wagtail.blocks.RichTextBlock', (), {'icon': 'edit'}), 1: ('wagtail.blocks.RichTextBlock', (), {}), 2: ('wagtail.blocks.CharBlock', (), {'help_text': '\n ID will be auto-generated on save.\n However, you may enter some human-friendly text that\n will be incorporated to make it easier to read.\n ', 'label': 'ID for this content block', 'required': False}), 3: ('wagtail.blocks.StructBlock', [[('link_id', 2)]], {}), 4: ('wagtail.blocks.StructBlock', [[('content_block', 1), ('anchor_link', 3)]], {}), 5: ('wagtail_footnotes.blocks.RichTextBlockWithFootnotes', (), {'features': ['anchor-identifier', 'h2', 'h3', 'h4', 'h5', 'hr', 'ol', 'ul', 'bold', 'italic', 'superscript', 'blockquote', 'link', 'document-link', 'image', 'icon', 'footnotes']}), 6: ('wagtail.blocks.CharBlock', (), {'required': False}), 7: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4'), ('h5', 'H5')]}), 8: ('wagtail.blocks.CharBlock', (), {'help_text': 'Input the name of an icon to appear to the left of the heading. E.g., approved, help-round, etc. See full list of icons', 'required': False}), 9: ('wagtail.blocks.StructBlock', [[('text', 6), ('level', 7), ('icon', 8)]], {'required': False}), 10: ('wagtail.images.blocks.ImageChooserBlock', (), {'required': False}), 11: ('wagtail.blocks.CharBlock', (), {'help_text': "No character limit, but be as succinct as possible. If the image is decorative (i.e., a screenreader wouldn't have anything useful to say about it), leave this field blank.", 'required': False}), 12: ('wagtail.blocks.StructBlock', [[('upload', 10), ('alt', 11)]], {}), 13: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('full', 'Full width'), (470, '470px'), (270, '270px'), (170, '170px')]}), 14: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('right', 'right'), ('left', 'left')], 'help_text': 'Does not apply if the image is full-width'}), 15: ('wagtail.blocks.RichTextBlock', (), {'label': 'Caption', 'required': False}), 16: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': 'Check to add a horizontal rule line to bottom of inset.', 'label': 'Has bottom rule line', 'required': False}), 17: ('wagtail.blocks.StructBlock', [[('image', 12), ('image_width', 13), ('image_position', 14), ('text', 15), ('is_bottom_rule', 16)]], {}), 18: ('wagtail.blocks.MultipleChoiceBlock', [], {'choices': [('is_full_width', 'Display the table at full width'), ('stack_on_mobile', 'Stack the table columns on mobile')], 'required': False}), 19: ('wagtail.blocks.CharBlock', (), {}), 20: ('wagtail.blocks.FloatBlock', (), {}), 21: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'ol', 'ul', 'link', 'document-link', 'superscript']}), 22: ('wagtail_footnotes.blocks.RichTextBlockWithFootnotes', (), {'features': ['bold', 'italic', 'ol', 'ul', 'link', 'document-link', 'superscript', 'footnotes']}), 23: ('wagtail.contrib.typed_table_block.blocks.TypedTableBlock', [[('text', 19), ('numeric', 20), ('rich_text', 21), ('rich_text_with_footnotes', 22)]], {}), 24: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'document-link'], 'required': False}), 25: ('wagtail.blocks.StructBlock', [[('heading', 9), ('text_introduction', 6), ('options', 18), ('data', 23), ('caption', 24)]], {}), 26: ('wagtail.blocks.TextBlock', (), {}), 27: ('wagtail.blocks.TextBlock', (), {'required': False}), 28: ('wagtail.blocks.StructBlock', [[('body', 26), ('citation', 27)]], {}), 29: ('wagtail.blocks.RichTextBlock', (), {'required': False}), 30: ('wagtail.blocks.CharBlock', (), {'help_text': 'Add an ARIA label if the link text does not describe the destination of the link (e.g. has ambiguous text like "Learn more" that is not descriptive on its own).', 'required': False}), 31: ('wagtail.blocks.CharBlock', (), {'default': '/', 'required': False}), 32: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'required': False}), 33: ('wagtail.blocks.StructBlock', [[('text', 6), ('aria_label', 30), ('url', 31), ('is_link_boldface', 32)]], {}), 34: ('wagtail.blocks.StructBlock', [[('slug_text', 6), ('paragraph_text', 29), ('button', 33)]], {}), 35: ('wagtail.blocks.ListBlock', (33,), {}), 36: ('wagtail.blocks.StructBlock', [[('heading', 6), ('paragraph', 29), ('links', 35)]], {}), 37: ('v1.blocks.ReusableTextChooserBlock', ('v1.ReusableText',), {}), 38: ('v1.blocks.ReusableNotificationChooserBlock', ('v1.ReusableNotification',), {}), 39: ('v1.blocks.EmailSignUpChooserBlock', (), {}), 40: ('wagtail.blocks.RichTextBlock', (), {'label': 'Well', 'required': False}), 41: ('wagtail.blocks.StructBlock', [[('content', 40)]], {}), 42: ('wagtail.blocks.StreamBlock', [[('content', 0), ('content_with_anchor', 4), ('content_with_footnotes', 5), ('heading', 9), ('image', 17), ('table', 25), ('quote', 28), ('cta', 34), ('related_links', 36), ('reusable_text', 37), ('reusable_notification', 38), ('email_signup', 39), ('well', 41)]], {}), 43: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('50-50', '50/50'), ('33-33-33', '33/33/33'), ('25-75', '25/75')], 'help_text': 'Choose the number and width of info unit columns.', 'label': 'Format'}), 44: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': "Check this to link all images and headings to the URL of the first link in their unit's list, if there is a link.", 'required': False}), 45: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to add a horizontal rule line to top of info unit group.', 'required': False}), 46: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this to show horizontal rule lines between info units.', 'label': 'Show rule lines between items', 'required': False}), 47: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('none', 'None'), ('rounded', 'Rounded corners'), ('circle', 'Circle')], 'help_text': 'Adds a border-radius class to images in this group, allowing for a rounded or circular border.', 'label': 'Border radius for images?', 'required': False}), 48: ('wagtail.blocks.StructBlock', [[('text', 6), ('level', 7), ('icon', 8)]], {'default': {'level': 'h3'}, 'required': False}), 49: ('wagtail.blocks.RichTextBlock', (), {'blank': True, 'required': False}), 50: ('wagtail.blocks.ListBlock', (33,), {'required': False}), 51: ('wagtail.blocks.StructBlock', [[('image', 12), ('heading', 48), ('body', 49), ('links', 50)]], {}), 52: ('wagtail.blocks.ListBlock', (51,), {'default': []}), 53: ('wagtail.blocks.StructBlock', [[('format', 43), ('heading', 9), ('intro', 29), ('link_image_and_heading', 44), ('has_top_rule_line', 45), ('lines_between_items', 46), ('border_radius_image', 47), ('info_units', 52)]], {}), 54: ('wagtail.blocks.BooleanBlock', (), {'required': False}), 55: ('wagtail.blocks.StreamBlock', [[('paragraph', 29), ('well', 41), ('links', 33), ('info_unit_group', 53)]], {'blank': True}), 56: ('wagtail.blocks.StructBlock', [[('label', 6), ('icon', 6), ('is_bordered', 54), ('is_midtone', 54), ('is_expanded', 54), ('is_expanded_padding', 54), ('content', 55)]], {}), 57: ('wagtail.blocks.RegexBlock', (), {'error_messages': {'invalid': 'The YouTube video ID is in the wrong format.'}, 'help_text': 'Enter the YouTube video ID, which is located at the end of the video URL, after "v=". For example, the video ID for https://www.youtube.com/watch?v=1V0Ax9OIc84 is 1V0Ax9OIc84.', 'label': 'YouTube video ID', 'regex': '^[\\w-]{11}$', 'required': False}), 58: ('wagtail.images.blocks.ImageChooserBlock', (), {'help_text': 'Optional thumbnail image to show before and after the video plays. If the thumbnail image is not set here, the video player will default to showing the thumbnail that was set in (or automatically chosen by) YouTube.', 'required': False}), 59: ('wagtail.blocks.StructBlock', [[('video_id', 57), ('thumbnail_image', 58)]], {}), 60: ('wagtail.blocks.CharBlock', (), {'required': True}), 61: ('wagtail.blocks.TextBlock', (), {'help_text': 'Accessible description of the chart content', 'required': True}), 62: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('bar', 'Bar'), ('datetime', 'Date/time'), ('line', 'Line'), ('tilemap', 'Tile grid map')]}), 63: ('wagtail.blocks.TextBlock', (), {'help_text': "URL of the chart's data source or an array of JSON data", 'required': True, 'rows': 2}), 64: ('wagtail.blocks.TextBlock', (), {'help_text': 'For charts pulling from a separate source file, include a list of the column headers (from a CSV file) or keys (from a JSON file) to include in the chart as ["HEADER/KEY1", "HEADER/KEY2"]. To change how the data is labeled in the chart, include the correct labels with the format [{"key": "HEADER/KEY1", "label": "NEWLABEL"}, {"key": "HEADER/KEY2", "label": "NEWLABEL2"}]', 'required': False}), 65: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': 'Uncheck this option to initially only show the first data series in the chart. Leave checked to show all data series by default. Users can always turn data series on or off by interacting with the chart legend. ', 'required': False}), 66: ('wagtail.blocks.TextBlock', (), {'help_text': 'The column header (CSV), key or data array (JSON) to include as the source of x-axis values.', 'required': False}), 67: ('wagtail.blocks.CharBlock', (), {'help_text': "Name the javascript function in chart-hooks.js to run on the provided data before handing it to the chart. Can also provide '___'-separated arguments to this function which are passed in as arguments 2 to n", 'required': False}), 68: ('wagtail.blocks.TextBlock', (), {'help_text': 'If the chart needs the option for users to filter the data shown, for example by date or geographic region, provide the JSON objects to filter on, in the format {key: "KEY", "label": "LABEL"}', 'required': False}), 69: ('wagtail.blocks.TextBlock', (), {'help_text': 'A JSON object with style overrides for the underlying Highcharts chart. No object merging is done, nested objects should be referenced with dot notation: {"tooltip.shape": "circle"}', 'required': False}), 70: ('wagtail.blocks.IntegerBlock', (), {'blank': True, 'help_text': 'A number to determine how many months of the data are projected values', 'max_value': 12, 'min_value': 0, 'null': True, 'required': False}), 71: ('wagtail.blocks.CharBlock', (), {'help_text': 'Attribution for the data source', 'required': False}), 72: ('wagtail.blocks.CharBlock', (), {'help_text': 'When the underlying data was published', 'required': False}), 73: ('wagtail.blocks.CharBlock', (), {'help_text': 'Custom text for the chart download field. Required to display a download link.', 'required': False}), 74: ('wagtail.blocks.CharBlock', (), {'help_text': 'Location of a file to download, if different from the data source', 'required': False}), 75: ('wagtail.blocks.TextBlock', (), {'help_text': 'General chart information', 'required': False}), 76: ('wagtail.blocks.StructBlock', [[('title', 60), ('subtitle', 27), ('description', 61), ('figure_number', 6), ('chart_type', 62), ('data_source', 63), ('data_series', 64), ('show_all_series_by_default', 65), ('x_axis_source', 66), ('transform', 67), ('x_axis_label', 6), ('y_axis_label', 6), ('filters', 68), ('style_overrides', 69), ('projected_months', 70), ('source_credits', 71), ('date_published', 72), ('download_text', 73), ('download_file', 74), ('notes', 75)]], {}), 77: ('wagtail.blocks.RichTextBlock', (), {'blank': True, 'features': ['ol', 'ul', 'bold', 'italic', 'link', 'document-link'], 'required': False}), 78: ('wagtail.blocks.CharBlock', (), {'blank': True, 'help_text': "Add an optional anchor link tag for this question. Tag should be unique and use dashes or underscores for separation instead of spaces (ie, 'question-one-tag')", 'max_length': 500, 'required': False}), 79: ('wagtail.blocks.CharBlock', (), {'max_length': 500}), 80: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'h3', 'h4', 'link', 'ol', 'ul', 'document-link', 'image', 'embed'], 'label': 'Text'}), 81: ('wagtail.blocks.StructBlock', [[('content', 80)]], {}), 82: ('wagtail.blocks.StreamBlock', [[('text', 81), ('table', 25), ('video_player', 59)]], {}), 83: ('wagtail.blocks.StructBlock', [[('anchor_tag', 78), ('question', 79), ('answer_content', 82)]], {}), 84: ('wagtail.blocks.ListBlock', (83,), {}), 85: ('wagtail.blocks.StructBlock', [[('description', 77), ('questions', 84)]], {'label': 'FAQ schema'}), 86: ('wagtail.blocks.CharBlock', (), {'label': 'Title of How To section', 'max_length': 500}), 87: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], 'help_text': 'Choose a tag for the title of the How To section.', 'label': 'Tag for How To section title'}), 88: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'help_text': 'The How To schema requires a title to let search engines understand what it is about. If you do not want the title to be displayed in the page, uncheck this box and the title content will only be made available to crawlers and screen readers.', 'label': 'Show How To section title', 'required': False}), 89: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4'), ('b', 'Bold'), ('p', 'Paragraph')], 'help_text': 'Choose a tag for the title of each HowTo step.', 'label': 'Tag for step titles'}), 90: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'help_text': 'Check this box to display numbers before step titles. ', 'label': 'Show numbers for steps', 'required': False}), 91: ('wagtail.blocks.CharBlock', (), {'blank': True, 'help_text': "Add an optional anchor link tag to allow linking directly to this step. Tag should be unique and use dashes or underscores for separation instead of spaces (ie, 'step-one-tag').", 'max_length': 500, 'required': False}), 92: ('wagtail.blocks.CharBlock', (), {'help_text': 'Enter a title for this HowTo step. You do not need to include a number in the title -- numbers will be added automatically in the template if the show numbers checkbox is checked.', 'max_length': 500}), 93: ('wagtail.blocks.StructBlock', [[('anchor_tag', 91), ('title', 92), ('step_content', 82)]], {}), 94: ('wagtail.blocks.ListBlock', (93,), {}), 95: ('wagtail.blocks.StructBlock', [[('title', 86), ('title_tag', 87), ('show_title', 88), ('description', 77), ('step_title_tag', 89), ('has_numbers', 90), ('steps', 94)]], {'label': 'HowTo schema', 'max_num': 1}), 96: ('wagtail.blocks.CharBlock', (), {'help_text': 'Optional: Adds an H5 eyebrow above H1 heading text. Only use in conjunction with heading.', 'label': 'Pre-heading', 'required': False}), 97: ('wagtail.blocks.RichTextBlock', (), {'icon': 'edit', 'required': False}), 98: ('wagtail.blocks.TextBlock', (), {'help_text': 'Accessible description of the chart content', 'required': False}), 99: ('wagtail.blocks.TextBlock', (), {'help_text': 'Description of the data source', 'required': False}), 100: ('wagtail.blocks.CharBlock', (), {'help_text': 'Location of a file to download', 'required': False}), 101: ('wagtail.blocks.TextBlock', (), {'help_text': 'Note about the chart', 'required': False}), 102: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('line', 'Line Chart'), ('bar', 'Vertical Bar Chart'), ('bar_horizontal', 'Horizontal Bar Chart')], 'label': 'Chart Type'}), 103: ('wagtail.blocks.TextBlock', (), {'default': '{"data":[], "options":{}}'}), 104: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Show legend', 'required': False}), 105: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Use HTML legend', 'required': False}), 106: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('top', 'Top'), ('bottom', 'Bottom'), ('left', 'Left'), ('right', 'Right')], 'group': 'General', 'label': 'Legend position'}), 107: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Reverse legend', 'required': False}), 108: ('wagtail.blocks.BooleanBlock', (), {'default': False, 'group': 'General', 'label': 'Show values on chart', 'required': False}), 109: ('wagtail.blocks.IntegerBlock', (), {'default': 1, 'group': 'General', 'label': 'Precision in labels/tooltips'}), 110: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'group': 'General', 'label': 'Show Chart Grid', 'required': False}), 111: ('wagtail.blocks.CharBlock', (), {'group': 'General', 'label': 'X axis label', 'required': False}), 112: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('none', 'No stacking'), ('stacked', 'Stacked'), ('stacked_100', 'Stacked 100%')], 'group': 'General', 'label': 'Stacking'}), 113: ('wagtail.blocks.CharBlock', (), {'group': 'General', 'label': 'Unit override', 'required': False}), 114: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis minimum value', 'required': False}), 115: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis maximum value', 'required': False}), 116: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis step size', 'required': False}), 117: ('wagtail.blocks.CharBlock', (), {'group': 'Left_Axis', 'label': 'Left Y axis label', 'required': False}), 118: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('number', 'Numerical'), ('percentage', 'Percentage')], 'group': 'Left_Axis', 'label': 'Left Y axis data type', 'required': False}), 119: ('wagtail.blocks.IntegerBlock', (), {'default': 0, 'group': 'Left_Axis', 'label': 'Left Y axis tick precision'}), 120: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'group': 'Left_Axis', 'label': 'Show left axis numbers', 'required': False}), 121: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis minimum value', 'required': False}), 122: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis maximum value', 'required': False}), 123: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis step size', 'required': False}), 124: ('wagtail.blocks.CharBlock', (), {'group': 'Right_Axis', 'label': 'Right Y axis label', 'required': False}), 125: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('number', 'Numerical'), ('percentage', 'Percentage')], 'group': 'Right_Axis', 'label': 'Right Y axis data type', 'required': False}), 126: ('wagtail.blocks.IntegerBlock', (), {'default': 0, 'group': 'Right_Axis', 'label': 'Right Y axis tick precision'}), 127: ('wagtail.blocks.BooleanBlock', (), {'default': True, 'group': 'Right_Axis', 'label': 'Show right axis numbers', 'required': False}), 128: ('wagtail.blocks.IntegerBlock', (), {'default': 2, 'group': 'Pie_Chart', 'label': 'Width of pie slice border'}), 129: ('wagtail.blocks.CharBlock', (), {'default': '#fff', 'group': 'Pie_Chart', 'label': 'Color of pie slice border'}), 130: ('wagtail.blocks.StructBlock', [[('show_legend', 104), ('html_legend', 105), ('legend_position', 106), ('reverse_legend', 107), ('show_values_on_chart', 108), ('precision', 109), ('show_grid', 110), ('x_label', 111), ('stacking', 112), ('unit_override', 113), ('y_left_min', 114), ('y_left_max', 115), ('y_left_step_size', 116), ('y_left_label', 117), ('y_left_data_type', 118), ('y_left_precision', 119), ('y_left_show', 120), ('y_right_min', 121), ('y_right_max', 122), ('y_right_step_size', 123), ('y_right_label', 124), ('y_right_data_type', 125), ('y_right_precision', 126), ('y_right_show', 127), ('pie_border_width', 128), ('pie_border_color', 129)]], {}), 131: ('wagtail.blocks.StructBlock', [[('eyebrow', 96), ('title', 9), ('intro', 97), ('description', 98), ('data_source', 99), ('date_published', 72), ('download_text', 73), ('download_file', 100), ('notes', 101), ('chart_type', 102), ('datasets', 103), ('settings', 130)]], {'colors': (('#20aa3f', 'CFPB Green'), ('#254b87', 'Navy'), ('#7eb7e8', 'Pacific 60'), ('#ffb858', 'Gold 80'), ('#c55998', 'Purple 80'), ('#addc91', 'Green 60'), ('#1fa040', 'Mid Dark Green'), ('#257675', 'Teal'), ('#89b6b5', 'Teal 60'), ('#d14124', 'Red'), ('#e79e8e', 'Red 60'), ('#0072ce', 'Pacific'), ('#254b87', 'Navy'), ('#dc731c', 'Dark Gold'), ('#745745', 'Dark Neutral'), ('#baa496', 'Neutral 60'), ('#dc9cbf', 'Purple 50'), ('#a01b68', 'Dark Purple'), ('#d2d3d5', 'Gray 20'))}), 132: ('wagtail.blocks.RawHTMLBlock', (), {'help_text': 'Content from WordPress unescaped.'})}),
+ ),
+ ]
diff --git a/cfgov/v1/models/base.py b/cfgov/v1/models/base.py
index bb0cb9cb11..be1c7bddef 100644
--- a/cfgov/v1/models/base.py
+++ b/cfgov/v1/models/base.py
@@ -354,7 +354,7 @@ def page_js(self):
# Returns the JS files required by this page and its StreamField blocks.
@property
def media_js(self):
- return sorted(set(self.page_js + self.streamfield_media("js")))
+ return list(dict.fromkeys(self.page_js + self.streamfield_media("js")))
# Returns the CSS files required by this page and its StreamField blocks.
@property
diff --git a/cfgov/v1/models/blog_page.py b/cfgov/v1/models/blog_page.py
index eb265bee89..c6fed062c7 100644
--- a/cfgov/v1/models/blog_page.py
+++ b/cfgov/v1/models/blog_page.py
@@ -5,7 +5,7 @@
from wagtail.fields import StreamField
from v1 import blocks as v1_blocks
-from v1.atomic_elements import organisms, schema
+from v1.atomic_elements import charts, organisms, schema
from v1.feeds import get_appropriate_rss_feed_url_for_page
from v1.models.learn_page import AbstractFilterPage
@@ -20,6 +20,7 @@ class BlogContent(blocks.StreamBlock):
simple_chart = organisms.SimpleChart()
faq_schema = schema.FAQ(label="FAQ schema")
how_to_schema = schema.HowTo(label="HowTo schema", max_num=1)
+ wagtailchart_block = charts.ChartBlock()
class BlogPage(AbstractFilterPage):
diff --git a/cfgov/v1/models/browse_page.py b/cfgov/v1/models/browse_page.py
index 5dd744d044..40e14f6428 100644
--- a/cfgov/v1/models/browse_page.py
+++ b/cfgov/v1/models/browse_page.py
@@ -11,7 +11,7 @@
from data_research.blocks import MortgageDataDownloads
from jobmanager.blocks import JobListingTable
-from v1.atomic_elements import molecules, organisms, schema
+from v1.atomic_elements import charts, molecules, organisms, schema
from v1.models.base import CFGOVPage
@@ -123,19 +123,32 @@ class BrowsePage(AbstractBrowsePage):
[
("full_width_text", organisms.FullWidthText()),
("info_unit_group", organisms.InfoUnitGroup()),
- ("simple_chart", organisms.SimpleChart()),
+ ("wagtailchart_block", charts.ChartBlock()),
("expandable_group", organisms.ExpandableGroup()),
("expandable", organisms.Expandable()),
("well", organisms.Well()),
("video_player", organisms.VideoPlayer()),
("table", organisms.Table()),
("raw_html_block", blocks.RawHTMLBlock(label="Raw HTML block")),
- ("chart_block", organisms.ChartBlock()),
- ("mortgage_chart_block", organisms.MortgageChartBlock()),
- ("mortgage_map_block", organisms.MortgageMapBlock()),
- ("mortgage_downloads_block", MortgageDataDownloads()),
- ("data_snapshot", organisms.DataSnapshot()),
- ("job_listing_table", JobListingTable()),
+ ("simple_chart", organisms.SimpleChart(group="Not commonly used")),
+ ("chart_block", organisms.ChartBlock(group="Not commonly used")),
+ (
+ "mortgage_chart_block",
+ organisms.MortgageChartBlock(group="Not commonly used"),
+ ),
+ (
+ "mortgage_map_block",
+ organisms.MortgageMapBlock(group="Not commonly used"),
+ ),
+ (
+ "mortgage_downloads_block",
+ MortgageDataDownloads(group="Not commonly used"),
+ ),
+ (
+ "data_snapshot",
+ organisms.DataSnapshot(group="Not commonly used"),
+ ),
+ ("job_listing_table", JobListingTable(group="Not commonly used")),
("faq_group", schema.FAQGroup()),
],
blank=True,
diff --git a/cfgov/v1/models/learn_page.py b/cfgov/v1/models/learn_page.py
index 38005d4ca9..a02e3783d9 100644
--- a/cfgov/v1/models/learn_page.py
+++ b/cfgov/v1/models/learn_page.py
@@ -23,7 +23,7 @@
from localflavor.us.models import USStateField
from v1 import blocks as v1_blocks
-from v1.atomic_elements import molecules, organisms, schema
+from v1.atomic_elements import charts, molecules, organisms, schema
from v1.models.base import CFGOVPage
from v1.util.events import get_venue_coords
@@ -122,6 +122,7 @@ class DocumentDetailPage(AbstractFilterPage):
("table", organisms.Table()),
("crc_table", organisms.ConsumerReportingCompanyTable()),
("case_docket_table", organisms.CaseDocketTable()),
+ ("wagtailchart_block", charts.ChartBlock()),
],
blank=True,
block_counts={"case_docket_table": {"max_num": 1}},
diff --git a/cfgov/v1/tests/atomic_elements/organisms/test_charts.py b/cfgov/v1/tests/atomic_elements/organisms/test_charts.py
new file mode 100644
index 0000000000..7e982fe70c
--- /dev/null
+++ b/cfgov/v1/tests/atomic_elements/organisms/test_charts.py
@@ -0,0 +1,23 @@
+from django.test import TestCase
+
+from wagtail.models import Site
+
+from v1.atomic_elements.charts import CHART_COLORS, CHART_TYPES, ChartBlock
+
+
+class ChartBlockTestCase(TestCase):
+ def setUp(self):
+ self.root = Site.objects.get(is_default_site=True).root_page
+
+ def test_chartblock_overrides(self):
+ block = ChartBlock()
+
+ # We override the chart types and colors
+ self.assertEqual(block.chart_types, CHART_TYPES)
+ self.assertEqual(block.meta.colors, CHART_COLORS)
+
+ # And we override the child block ordering to ensure these are the
+ # last three blocks in the editor.
+ self.assertEqual(list(block.child_blocks.keys())[-1], "settings")
+ self.assertEqual(list(block.child_blocks.keys())[-2], "datasets")
+ self.assertEqual(list(block.child_blocks.keys())[-3], "chart_type")
diff --git a/npm-packages-offline-cache/patternomaly-1.3.2.tgz b/npm-packages-offline-cache/patternomaly-1.3.2.tgz
new file mode 100644
index 0000000000..a0cb4bbe8a
Binary files /dev/null and b/npm-packages-offline-cache/patternomaly-1.3.2.tgz differ
diff --git a/npm-packages-offline-cache/patternomaly-36e497c0b79ca786c8eded8abd954d927facd6a7 b/npm-packages-offline-cache/patternomaly-36e497c0b79ca786c8eded8abd954d927facd6a7
new file mode 100644
index 0000000000..8cabdd47d0
Binary files /dev/null and b/npm-packages-offline-cache/patternomaly-36e497c0b79ca786c8eded8abd954d927facd6a7 differ
diff --git a/npm-packages-offline-cache/patternomaly-5ca857ae7888aacaac9ecfc43f6b3e9859e40645 b/npm-packages-offline-cache/patternomaly-5ca857ae7888aacaac9ecfc43f6b3e9859e40645
new file mode 100644
index 0000000000..c3962d5a9b
Binary files /dev/null and b/npm-packages-offline-cache/patternomaly-5ca857ae7888aacaac9ecfc43f6b3e9859e40645 differ
diff --git a/package.json b/package.json
index 4001ae8d2d..540a011ad5 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"lodash.throttle": "4.1.1",
"number-to-words": "1.2.4",
"papaparse": "5.3.2",
+ "patternomaly": "1.3.2",
"postcss": "8.4.49",
"svg-inline-loader": "0.8.2"
},
diff --git a/requirements/libraries.txt b/requirements/libraries.txt
index b49016785f..db630d4403 100644
--- a/requirements/libraries.txt
+++ b/requirements/libraries.txt
@@ -43,6 +43,7 @@ wagtail-placeholder-images==0.1.1
wagtail-sharing==2.12.1
wagtail-treemodeladmin==1.9.2
wagtailmedia==0.15.2
+wagtailcharts==0.6.1
# These packages are installed from GitHub.
https://github.com/cfpb/owning-a-home-api/releases/download/0.18.2/owning_a_home_api-0.18.2-py3-none-any.whl
diff --git a/yarn.lock b/yarn.lock
index b7feea29e3..8a018c614b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,6 +10,7 @@
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
@@ -4534,6 +4535,11 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+patternomaly@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/patternomaly/-/patternomaly-1.3.2.tgz#70b8db17d7318ab1471cc43f94011bb866c54d09"
+ integrity sha512-70UhA5+ZrnNgdfDBKXIGbMHpP+naTzfx9vPT4KwIdhtWWs0x6FWZRJQMXXhV2jcK0mxl28FA/2LPAKArNG058Q==
+
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"